aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2022-03-10 12:19:59 +0100
committerRichard Moe Gustavsen <richard.gustavsen@qt.io>2022-03-10 12:19:59 +0100
commite798058823d6b3b813807151b91ee7d4c9417c3e (patch)
treea77487803177ea40775343e87b1ede3e521f0194
parent56993597c05545922bbb2ef7aed49e7e34ac06b9 (diff)
parent473663128eaaf1c92ada255ac6ef1101c8bda790 (diff)
Merge remote-tracking branch 'origin/dev' into iosstyle
-rw-r--r--cmake/QtDeclarativeSetup.cmake8
-rw-r--r--dependencies.yaml10
-rw-r--r--examples/quick/quickwidgets/qquickviewcomparison/main.cpp2
-rw-r--r--examples/quick/quickwidgets/quickwidget/main.cpp2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_d3d11/main.cpp2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/main.cpp2
-rw-r--r--examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp2
-rw-r--r--examples/quick/scenegraph/d3d11underqml/main.cpp2
-rw-r--r--examples/quick/scenegraph/metaltextureimport/main.cpp2
-rw-r--r--examples/quick/scenegraph/metalunderqml/main.cpp2
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalsquircle.mm2
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/main.cpp2
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/main.cpp2
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp2
-rw-r--r--src/3rdparty/masm/assembler/X86Assembler.h146
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp3
-rw-r--r--src/imports/builtins/CMakeLists.txt7
-rw-r--r--src/imports/builtins/builtins.qmltypes28
-rw-r--r--src/labs/models/qqmltablemodel_p.h1
-rw-r--r--src/labs/models/qqmltablemodelcolumn_p.h1
-rw-r--r--src/particles/qquickparticleaffector_p.h1
-rw-r--r--src/particles/qquickparticlesystem_p.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt2
-rw-r--r--src/plugins/scenegraph/openvg/CMakeLists.txt2
-rw-r--r--src/qml/CMakeLists.txt5
-rw-r--r--src/qml/Qt6AndroidQmlMacros.cmake9
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake1
-rw-r--r--src/qml/Qt6QmlDeploySupport.cmake4
-rw-r--r--src/qml/Qt6QmlMacros.cmake75
-rw-r--r--src/qml/common/qv4compileddata_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h3
-rw-r--r--src/qml/debugger/qqmldebug.h1
-rw-r--r--src/qml/doc/snippets/code/doc_src_qtqml.cmake6
-rw-r--r--src/qml/doc/snippets/qmltc/CMakeLists.txt4
-rw-r--r--src/qml/doc/snippets/qmltc/myApp.qml1
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp6
-rw-r--r--src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp3
-rw-r--r--src/qml/doc/src/cmake/cmake-properties.qdoc187
-rw-r--r--src/qml/doc/src/cmake/cmake-variables.qdoc1
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc12
-rw-r--r--src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc22
-rw-r--r--src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc5
-rw-r--r--src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc (renamed from src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc)23
-rw-r--r--src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc2
-rw-r--r--src/qml/doc/src/cmake/qt_query_qml_module.qdoc3
-rw-r--r--src/qml/doc/src/cmake/qt_target_qml_sources.qdoc2
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc6
-rw-r--r--src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake4
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc18
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc8
-rw-r--r--src/qml/doc/src/qtqml.qdoc7
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsapi/qjsengine.cpp140
-rw-r--r--src/qml/jsapi/qjsengine.h28
-rw-r--r--src/qml/jsruntime/qv4engine.cpp139
-rw-r--r--src/qml/jsruntime/qv4engine_p.h17
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp7
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h2
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h6
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp5
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h1
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference.cpp5
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference_p.h2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp41
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h9
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp14
-rw-r--r--src/qml/parser/qqmljs.g3
-rw-r--r--src/qml/parser/qqmljslexer.cpp877
-rw-r--r--src/qml/parser/qqmljslexer_p.h117
-rw-r--r--src/qml/qml/qqml.cpp354
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp26
-rw-r--r--src/qml/qml/qqmlcomponent.h3
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h11
-rw-r--r--src/qml/qml/qqmlengine.cpp38
-rw-r--r--src/qml/qml/qqmlguard_p.h111
-rw-r--r--src/qml/qml/qqmlimport.cpp34
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp42
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp1
-rw-r--r--src/qml/qml/qqmlpluginimporter.cpp8
-rw-r--r--src/qml/qml/qqmlproperty.h2
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp24
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h3
-rw-r--r--src/qml/qml/qqmltypedata.cpp10
-rw-r--r--src/qml/qml/qqmltypeloader.cpp1
-rw-r--r--src/qml/qml/qqmltypeloader_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp21
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h5
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp17
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h8
-rw-r--r--src/qmlcompiler/CMakeLists.txt5
-rw-r--r--src/qmlcompiler/qdeferredpointer_p.h20
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp397
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h102
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp1130
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h28
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h140
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp25
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp18
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp18
-rw-r--r--src/qmlcompiler/qqmljsimporter_p.h11
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp434
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h3
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp22
-rw-r--r--src/qmlcompiler/qqmljsloadergenerator.cpp2
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp133
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h90
-rw-r--r--src/qmlcompiler/qqmljsmetatypes.cpp108
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h240
-rw-r--r--src/qmlcompiler/qqmljsregistercontent.cpp22
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h48
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp11
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h12
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp12
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp44
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp2
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp994
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h27
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp678
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h67
-rw-r--r--src/qmldebug/qqmldebugconnection.cpp1
-rw-r--r--src/qmldebug/qqmlenginecontrolclient_p_p.h2
-rw-r--r--src/qmldom/qqmldomlinewriter_p.h1
-rw-r--r--src/qmldom/qqmldomtypesreader.cpp2
-rw-r--r--src/qmlintegration/qqmlintegration.h2
-rw-r--r--src/qmlmodels/CMakeLists.txt5
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp8
-rw-r--r--src/qmlmodels/qqmladaptormodel_p.h4
-rw-r--r--src/qmlmodels/qqmlmodelindexvaluetype.cpp (renamed from src/qml/types/qqmlmodelindexvaluetype.cpp)0
-rw-r--r--src/qmlmodels/qqmlmodelindexvaluetype_p.h (renamed from src/qml/types/qqmlmodelindexvaluetype_p.h)0
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel.cpp57
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel_p_p.h4
-rw-r--r--src/qmlmodels/qquickpackage.cpp9
-rw-r--r--src/qmltest/CMakeLists.txt2
-rw-r--r--src/qmltest/quicktest.cpp2
-rw-r--r--src/qmltest/quicktestresult_p.h2
-rw-r--r--src/qmltest/quicktestutil.cpp2
-rw-r--r--src/qmltyperegistrar/qmltyperegistrar.cpp17
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp34
-rw-r--r--src/quick/CMakeLists.txt4
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp7
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc12
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc (renamed from src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc)0
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc1
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc14
-rw-r--r--src/quick/doc/src/qt6-changes.qdoc4
-rw-r--r--src/quick/doc/src/qtquick.qdoc27
-rw-r--r--src/quick/items/context2d/qquickcanvascontext_p.h6
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h1
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h5
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp5
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h2
-rw-r--r--src/quick/items/qquickitem.h1
-rw-r--r--src/quick/items/qquickloader.cpp12
-rw-r--r--src/quick/items/qquickloader_p.h1
-rw-r--r--src/quick/items/qquickloader_p_p.h4
-rw-r--r--src/quick/items/qquickrendercontrol.cpp1
-rw-r--r--src/quick/items/qquickrendercontrol.h2
-rw-r--r--src/quick/items/qquickstateoperations.cpp69
-rw-r--r--src/quick/items/qquicktableview.cpp326
-rw-r--r--src/quick/items/qquicktableview_p.h6
-rw-r--r--src/quick/items/qquicktableview_p_p.h48
-rw-r--r--src/quick/items/qquicktext.cpp49
-rw-r--r--src/quick/items/qquicktreeview.cpp315
-rw-r--r--src/quick/items/qquicktreeview_p.h16
-rw-r--r--src/quick/items/qquicktreeview_p_p.h1
-rw-r--r--src/quick/items/qquickview_p.h5
-rw-r--r--src/quick/items/qquickwindow.cpp95
-rw-r--r--src/quick/items/qquickwindow_p.h3
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp54
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp17
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h1
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h1
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp3
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h2
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp4
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp89
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h6
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture.cpp9
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp2
-rw-r--r--src/quick/util/qquickapplication.cpp31
-rw-r--r--src/quick/util/qquickapplication_p.h3
-rw-r--r--src/quick/util/qquickfontloader.cpp12
-rw-r--r--src/quick/util/qquickforeignutils_p.h9
-rw-r--r--src/quick/util/qquickinputmethod.cpp159
-rw-r--r--src/quick/util/qquickinputmethod_p.h122
-rw-r--r--src/quick/util/qquickpath_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp114
-rw-r--r--src/quick/util/qquickpixmapcache_p.h3
-rw-r--r--src/quick/util/qquickstate_p_p.h12
-rw-r--r--src/quick/util/qquickstategroup.cpp10
-rw-r--r--src/quick/util/qquickvalidator.cpp6
-rw-r--r--src/quickcontrols2/CMakeLists.txt3
-rw-r--r--src/quickcontrols2/basic/DayOfWeekRow.qml4
-rw-r--r--src/quickcontrols2/basic/MonthGrid.qml4
-rw-r--r--src/quickcontrols2/basic/WeekNumberColumn.qml4
-rw-r--r--src/quickcontrols2/doc/src/qt6-changes.qdoc16
-rw-r--r--src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc8
-rw-r--r--src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc14
-rw-r--r--src/quickcontrols2/material/qquickmaterialstyle_p.h2
-rw-r--r--src/quickcontrols2impl/qquickiconlabel.cpp2
-rw-r--r--src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc4
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp1
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp9
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h1
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml4
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml19
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml16
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml2
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp8
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp12
-rw-r--r--src/quicknativestyle/items/qquickstyleitem.h2
-rw-r--r--src/quicktemplates2/qquickabstractbutton.cpp6
-rw-r--r--src/quicktemplates2/qquickaction.cpp2
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp50
-rw-r--r--src/quicktemplates2/qquickicon.cpp69
-rw-r--r--src/quicktemplates2/qquickicon_p.h5
-rw-r--r--src/quicktemplates2/qquickmenuitem.cpp3
-rw-r--r--src/quicktemplates2/qquickpresshandler.cpp3
-rw-r--r--src/quicktemplates2/qquickscrollbar.cpp20
-rw-r--r--src/quicktemplates2/qquickspinbox.cpp3
-rw-r--r--src/quicktemplates2/qquicktreeviewdelegate.cpp7
-rw-r--r--src/quickwidgets/qquickwidget.cpp45
-rw-r--r--sync.profile1
-rw-r--r--tests/auto/CMakeLists.txt2
-rw-r--r--tests/auto/cmake/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/empty_qmldir/CMakeLists.txt5
-rw-r--r--tests/auto/core/CMakeLists.txt4
-rw-r--r--tests/auto/qml/CMakeLists.txt11
-rw-r--r--tests/auto/qml/bindingdependencyapi/dummy_imports.qml7
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp4
-rw-r--r--tests/auto/qml/parserstress/CMakeLists.txt6
-rw-r--r--tests/auto/qml/parserstress/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qjsengine/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsengine/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp38
-rw-r--r--tests/auto/qml/qjsmanagedvalue/CMakeLists.txt5
-rw-r--r--tests/auto/qml/qjsmanagedvalue/dummy_imports.qml4
-rw-r--r--tests/auto/qml/qjsvalue/CMakeLists.txt5
-rw-r--r--tests/auto/qml/qjsvalue/dummy_imports.qml4
-rw-r--r--tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp17
-rw-r--r--tests/auto/qml/qmlcppcodegen/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/blockComments.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversions2.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h78
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/functionLookup.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/infinities.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/isnan.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectInVar.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/typedArray.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp151
-rw-r--r--tests/auto/qml/qmldiskcache/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qmlformat/BLACKLIST4
-rw-r--r--tests/auto/qml/qmllint/data/Things/qmldir2
-rw-r--r--tests/auto/qml/qmllint/data/callJSValueProp.qml6
-rw-r--r--tests/auto/qml/qmllint/data/callVarProp.qml7
-rw-r--r--tests/auto/qml/qmllint/data/defaultImport.qml (renamed from tests/auto/qml/qmllint/data/optionalImport.qml)0
-rw-r--r--tests/auto/qml/qmllint/data/incompleteQmltypes3.qml6
-rw-r--r--tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml10
-rw-r--r--tests/auto/qml/qmllint/data/multilineStringTortureTick.qml10
-rw-r--r--tests/auto/qml/qmllint/data/qmodelIndex.qml6
-rw-r--r--tests/auto/qml/qmllint/data/shadowedMethod.qml8
-rw-r--r--tests/auto/qml/qmllint/data/shadowedSignal.qml6
-rw-r--r--tests/auto/qml/qmllint/data/shadowedSignalWithId.qml6
-rw-r--r--tests/auto/qml/qmllint/data/shadowedSlot.qml6
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp1177
-rw-r--r--tests/auto/qml/qmltc/BLACKLIST2
-rw-r--r--tests/auto/qml/qmltc/data/CMakeLists.txt8
-rw-r--r--tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp (renamed from tools/qmltc/prototype/visitor.h)41
-rw-r--r--tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h136
-rw-r--r--tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h5
-rw-r--r--tests/auto/qml/qmltc/data/deferredProperties.qml16
-rw-r--r--tests/auto/qml/qmltc/data/deferredProperties_attached.qml6
-rw-r--r--tests/auto/qml/qmltc/data/deferredProperties_complex.qml9
-rw-r--r--tests/auto/qml/qmltc/data/deferredProperties_group.qml6
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.cpp97
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.h1
-rw-r--r--tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp1
-rw-r--r--tests/auto/qml/qmltc_qprocess/CMakeLists.txt5
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml7
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml4
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp15
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt10
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp40
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h25
-rw-r--r--tests/auto/qml/qqmlapplicationengine/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp4
-rw-r--r--tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs3
-rw-r--r--tests/auto/qml/qqmlconsole/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlengine/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qqmlfileselector/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlimport/data/absoluteImport.qml4
-rw-r--r--tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml4
-rw-r--r--tests/auto/qml/qqmlimport/data/relativeResourceImport.qml4
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp33
-rw-r--r--tests/auto/qml/qqmlincubator/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlinfo/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlitemmodels/data/modelindex.qml1
-rw-r--r--tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml1
-rw-r--r--tests/auto/qml/qqmllanguage/data/foreignExtended.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h14
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp88
-rw-r--r--tests/auto/qml/qqmllistreference/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlmetaobject/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp15
-rw-r--r--tests/auto/qml/qqmlmetatype/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qqmlnotifier/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlpromise/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlpropertymap/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp15
-rw-r--r--tests/auto/qml/qqmlsqldatabase/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qqmltimer/dummy_imports.qml9
-rw-r--r--tests/auto/qml/qqmltypeloader/dummy_imports.qml9
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp1
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml21
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp4
-rw-r--r--tests/auto/qml/qtqmlmodules/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp1
-rw-r--r--tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml6
-rw-r--r--tests/auto/quick/CMakeLists.txt1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp1
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp61
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp5
-rw-r--r--tests/auto/quick/qquickanimators/tst_qquickanimators.cpp1
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp1
-rw-r--r--tests/auto/quick/qquickbehaviors/bindable.h2
-rw-r--r--tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp10
-rw-r--r--tests/auto/quick/qquickdrag/tst_qquickdrag.cpp1
-rw-r--r--tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp1
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp3
-rw-r--r--tests/auto/quick/qquickframebufferobject/BLACKLIST8
-rw-r--r--tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp9
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp23
-rw-r--r--tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp1
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp1
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp6
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt36
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/data/test.qml (renamed from tools/qmltc/prototype/visitor.cpp)54
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp377
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem.h100
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h85
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp187
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/testrhiitem.h86
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/texture.frag18
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/texture.vert19
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp102
-rw-r--r--tests/auto/quick/qquicklayouts/BLACKLIST9
-rw-r--r--tests/auto/quick/qquicklistview/data/qtbug48044.qml2
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp2
-rw-r--r--tests/auto/quick/qquickloader/data/noEngine.qml32
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp15
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp47
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp10
-rw-r--r--tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp1
-rw-r--r--tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp3
-rw-r--r--tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp1
-rw-r--r--tests/auto/quick/qquickscreen/tst_qquickscreen.cpp1
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp45
-rw-r--r--tests/auto/quick/qquickstates/data/anchorRewind.qml31
-rw-r--r--tests/auto/quick/qquickstates/data/noStateOsciallation.qml22
-rw-r--r--tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml18
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp134
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp262
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp45
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp16
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp4
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.cpp11
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.h3
-rw-r--r--tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp350
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp18
-rw-r--r--tests/auto/quickcontrols2/CMakeLists.txt6
-rw-r--r--tests/auto/quickcontrols2/controls/basic/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols2/controls/basic/dummy.qml9
-rw-r--r--tests/auto/quickcontrols2/controls/basic/dummy_imports.qml12
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_button.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_checkbox.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_combobox.qml5
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml34
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_switch.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml4
-rw-r--r--tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols2/controls/fusion/dummy.qml9
-rw-r--r--tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml12
-rw-r--r--tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols2/controls/imagine/dummy.qml9
-rw-r--r--tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml12
-rw-r--r--tests/auto/quickcontrols2/controls/macos/CMakeLists.txt10
-rw-r--r--tests/auto/quickcontrols2/controls/material/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols2/controls/material/dummy.qml9
-rw-r--r--tests/auto/quickcontrols2/controls/material/dummy_imports.qml12
-rw-r--r--tests/auto/quickcontrols2/controls/universal/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols2/controls/universal/dummy.qml9
-rw-r--r--tests/auto/quickcontrols2/controls/universal/dummy_imports.qml12
-rw-r--r--tests/auto/quickcontrols2/controls/windows/CMakeLists.txt11
-rw-r--r--tests/auto/quickcontrols2/customization/dummy_imports.qml9
-rw-r--r--tests/auto/quickcontrols2/designer/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/designer/dummy_imports.qml8
-rw-r--r--tests/auto/quickcontrols2/pressandhold/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp21
-rw-r--r--tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml18
-rw-r--r--tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp20
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp1
-rw-r--r--tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp1
-rw-r--r--tests/auto/quickcontrols2/qquickiconimage/BLACKLIST15
-rw-r--r--tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp12
-rw-r--r--tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml13
-rw-r--r--tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml6
-rw-r--r--tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp13
-rw-r--r--tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp53
-rw-r--r--tests/auto/quickcontrols2/revisions/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/snippets/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml8
-rw-r--r--tests/auto/quickdialogs/CMakeLists.txt2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST3
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt1
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml9
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp1
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml10
-rw-r--r--tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml8
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml8
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt5
-rw-r--r--tests/manual/CMakeLists.txt1
-rw-r--r--tests/manual/treeview/sidebyside/data/treeview.qml36
-rw-r--r--tools/CMakeLists.txt2
-rw-r--r--tools/qml/main.cpp11
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp22
-rw-r--r--tools/qmlimportscanner/main.cpp1
-rw-r--r--tools/qmltc/CMakeLists.txt7
-rw-r--r--tools/qmltc/main.cpp58
-rw-r--r--tools/qmltc/prototype/codegenerator.cpp228
-rw-r--r--tools/qmltc/prototype/codegenerator.h51
-rw-r--r--tools/qmltc/prototype/codegeneratorutil.cpp19
-rw-r--r--tools/qmltc/prototype/codegeneratorutil.h14
-rw-r--r--tools/qmltc/prototype/codegeneratorwriter.cpp476
-rw-r--r--tools/qmltc/prototype/codegeneratorwriter.h57
-rw-r--r--tools/qmltc/prototype/generatedcodeprimitives.h126
-rw-r--r--tools/qmltc/prototype/qml2cppcontext.h13
-rw-r--r--tools/qmltc/prototype/qml2cppdefaultpasses.cpp56
-rw-r--r--tools/qmltc/prototype/qml2cppdefaultpasses.h6
-rw-r--r--tools/qmltc/prototype/qml2cpppropertyutils.h4
-rw-r--r--tools/qmltc/prototype/qmlcompiler.h179
-rw-r--r--tools/qmltc/prototype/typeresolver.h64
-rw-r--r--tools/qmltc/qmltccodewriter.cpp218
-rw-r--r--tools/qmltc/qmltccodewriter.h2
-rw-r--r--tools/qmltc/qmltccompiler.cpp72
-rw-r--r--tools/qmltc/qmltccompiler.h4
-rw-r--r--tools/qmltc/qmltccompilerpieces.h2
-rw-r--r--tools/qmltc/qmltcoutputir.h30
-rw-r--r--tools/qmltc/qmltctyperesolver.cpp (renamed from tools/qmltc/prototype/typeresolver.cpp)24
-rw-r--r--tools/qmltc/qmltctyperesolver.h24
-rw-r--r--tools/qmltc/qmltcvisitor.cpp46
-rw-r--r--tools/qmltc/qmltcvisitor.h4
468 files changed, 11627 insertions, 5414 deletions
diff --git a/cmake/QtDeclarativeSetup.cmake b/cmake/QtDeclarativeSetup.cmake
index 479c71d28a..3513fdad0b 100644
--- a/cmake/QtDeclarativeSetup.cmake
+++ b/cmake/QtDeclarativeSetup.cmake
@@ -3,7 +3,13 @@
# archive with the tree hash. For unreleased versions, we'll ask git
# rev-parse. If none of this works, we use CMake to hash all the files
# in the src/qml/ directory.
+# Skip recreation of the hash when doing a developer build.
function(qt_declarative_write_tag_header target_name)
+ set(out_file "${CMAKE_CURRENT_BINARY_DIR}/qml_compile_hash_p.h")
+ if(FEATURE_developer_build AND EXISTS "${out_file}")
+ return()
+ endif()
+
set(tag_file "${CMAKE_CURRENT_SOURCE_DIR}/../../.tag")
set(tag_contents "")
if(EXISTS "${tag_file}")
@@ -34,7 +40,7 @@ function(qt_declarative_write_tag_header target_name)
string(LENGTH "${QML_COMPILE_HASH}" QML_COMPILE_HASH_LENGTH)
if(QML_COMPILE_HASH_LENGTH GREATER 0)
- configure_file("qml_compile_hash_p.h.in" "${CMAKE_CURRENT_BINARY_DIR}/qml_compile_hash_p.h")
+ configure_file("qml_compile_hash_p.h.in" "${out_file}")
else()
message(FATAL_ERROR "QML compile hash is empty! "
"You need either a valid git repository or a non-empty .tag file.")
diff --git a/dependencies.yaml b/dependencies.yaml
index 5851ea78fe..81069ac139 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../qtbase:
- ref: de6ced66920600e659dbaa2509526a3bcb0b3360
+ ref: 71af0d7059d47a199e1d26de6573d6a5038caa30
required: true
../qtimageformats:
- ref: de304699bbec32172ecd0fe09fdc63c6b5121b18
+ ref: 15d372ed6b59685f9f43df31f9fc2b7969867031
required: false
../qtlanguageserver:
- ref: 662e65107b882e48a04fdf5ddc694c0c40861a41
+ ref: f97ba9b2e8a726bc503d98fcbd007bbb1985b156
required: false
../qtshadertools:
- ref: 996369f1fbbdc71d92b5cdb4e319e427ec907b48
+ ref: 7598391985188d2a33d276effd1c3cf3ef7f2666
required: false
../qtsvg:
- ref: a7b4ead92faa36891996dc20deb0e1b2b3f7a495
+ ref: 2503453da04181a350e2a2833fe0fdcd6e6141c4
required: false
diff --git a/examples/quick/quickwidgets/qquickviewcomparison/main.cpp b/examples/quick/quickwidgets/qquickviewcomparison/main.cpp
index e40c509829..c0a26eefba 100644
--- a/examples/quick/quickwidgets/qquickviewcomparison/main.cpp
+++ b/examples/quick/quickwidgets/qquickviewcomparison/main.cpp
@@ -61,7 +61,7 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
// this example and QQuickWidget are only functional when rendering with OpenGL
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
QCoreApplication::setApplicationName("Qt QQuickView/QQuickWidget Comparison Example");
QCoreApplication::setOrganizationName("QtProject");
diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp
index 1c8b21f790..191adcc451 100644
--- a/examples/quick/quickwidgets/quickwidget/main.cpp
+++ b/examples/quick/quickwidgets/quickwidget/main.cpp
@@ -198,7 +198,7 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
// this example and QQuickWidget are only functional when rendering with OpenGL
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
QCoreApplication::setApplicationName("Qt QQuickWidget Example");
QCoreApplication::setOrganizationName("QtProject");
diff --git a/examples/quick/rendercontrol/rendercontrol_d3d11/main.cpp b/examples/quick/rendercontrol/rendercontrol_d3d11/main.cpp
index 119b289500..defb080a91 100644
--- a/examples/quick/rendercontrol/rendercontrol_d3d11/main.cpp
+++ b/examples/quick/rendercontrol/rendercontrol_d3d11/main.cpp
@@ -64,7 +64,7 @@ int main(int argc, char **argv)
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
// only functional when Qt Quick is also using D3D11
- QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11Rhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
Engine engine;
if (!engine.create())
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp
index 11db82773b..e0315cef38 100644
--- a/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp
+++ b/examples/quick/rendercontrol/rendercontrol_opengl/main.cpp
@@ -57,7 +57,7 @@ int main(int argc, char **argv)
QGuiApplication app(argc, argv);
// only functional when Qt Quick is also using OpenGL
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
WindowSingleThreaded window;
window.resize(1024, 768);
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp
index 9acfbfff23..ef46c6361d 100644
--- a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp
+++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp
@@ -210,7 +210,7 @@ void SquircleRenderer::frameStart()
QSGRendererInterface *rif = m_window->rendererInterface();
// We are not prepared for anything other than running with the RHI and its D3D11 backend.
- Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Direct3D11Rhi);
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Direct3D11);
m_device = reinterpret_cast<ID3D11Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
Q_ASSERT(m_device);
diff --git a/examples/quick/scenegraph/d3d11underqml/main.cpp b/examples/quick/scenegraph/d3d11underqml/main.cpp
index d6870eed76..7b568156ae 100644
--- a/examples/quick/scenegraph/d3d11underqml/main.cpp
+++ b/examples/quick/scenegraph/d3d11underqml/main.cpp
@@ -56,7 +56,7 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
- QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11Rhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
diff --git a/examples/quick/scenegraph/metaltextureimport/main.cpp b/examples/quick/scenegraph/metaltextureimport/main.cpp
index 2abc1c5671..9fb9d9ba12 100644
--- a/examples/quick/scenegraph/metaltextureimport/main.cpp
+++ b/examples/quick/scenegraph/metaltextureimport/main.cpp
@@ -54,7 +54,7 @@
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
- QQuickWindow::setGraphicsApi(QSGRendererInterface::MetalRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Metal);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
diff --git a/examples/quick/scenegraph/metalunderqml/main.cpp b/examples/quick/scenegraph/metalunderqml/main.cpp
index b08f692ded..8914a012e6 100644
--- a/examples/quick/scenegraph/metalunderqml/main.cpp
+++ b/examples/quick/scenegraph/metalunderqml/main.cpp
@@ -56,7 +56,7 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
- QQuickWindow::setGraphicsApi(QSGRendererInterface::MetalRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Metal);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
index 4acedcab02..edac29665c 100644
--- a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
+++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
@@ -205,7 +205,7 @@ void SquircleRenderer::frameStart()
QSGRendererInterface *rif = m_window->rendererInterface();
// We are not prepared for anything other than running with the RHI and its Metal backend.
- Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Metal);
m_device = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
Q_ASSERT(m_device);
diff --git a/examples/quick/scenegraph/vulkantextureimport/main.cpp b/examples/quick/scenegraph/vulkantextureimport/main.cpp
index d2eb5184dc..290eccdc42 100644
--- a/examples/quick/scenegraph/vulkantextureimport/main.cpp
+++ b/examples/quick/scenegraph/vulkantextureimport/main.cpp
@@ -55,7 +55,7 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
- QQuickWindow::setGraphicsApi(QSGRendererInterface::VulkanRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
diff --git a/examples/quick/scenegraph/vulkanunderqml/main.cpp b/examples/quick/scenegraph/vulkanunderqml/main.cpp
index e4a3f36ca7..112332f4fd 100644
--- a/examples/quick/scenegraph/vulkanunderqml/main.cpp
+++ b/examples/quick/scenegraph/vulkanunderqml/main.cpp
@@ -56,7 +56,7 @@ int main(int argc, char **argv)
QGuiApplication app(argc, argv);
// This example needs Vulkan. It will not run otherwise.
- QQuickWindow::setGraphicsApi(QSGRendererInterface::VulkanRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
index c516489efa..082303d3b3 100644
--- a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
@@ -205,7 +205,7 @@ void SquircleRenderer::frameStart()
QSGRendererInterface *rif = m_window->rendererInterface();
// We are not prepared for anything other than running with the RHI and its Vulkan backend.
- Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi);
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Vulkan);
if (m_vert.isEmpty())
prepareShader(VertexStage);
diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h
index 1061021227..07a883397a 100644
--- a/src/3rdparty/masm/assembler/X86Assembler.h
+++ b/src/3rdparty/masm/assembler/X86Assembler.h
@@ -20,7 +20,7 @@
* 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.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef X86Assembler_h
@@ -164,7 +164,7 @@ private:
OP_HLT = 0xF4,
OP_GROUP3_EbIb = 0xF6,
OP_GROUP3_Ev = 0xF7,
- OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
+ OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
OP_GROUP5_Ev = 0xFF,
} OneByteOpcodeID;
@@ -220,12 +220,12 @@ private:
GROUP1_OP_CMP = 7,
GROUP1A_OP_POP = 0,
-
+
GROUP2_OP_ROL = 0,
GROUP2_OP_ROR = 1,
GROUP2_OP_RCL = 2,
GROUP2_OP_RCR = 3,
-
+
GROUP2_OP_SHL = 4,
GROUP2_OP_SHR = 5,
GROUP2_OP_SAR = 7,
@@ -246,7 +246,7 @@ private:
ESCAPE_DD_FSTP_doubleReal = 3,
} GroupOpcodeID;
-
+
class X86InstructionFormatter;
public:
@@ -308,7 +308,7 @@ public:
{
m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset);
}
-
+
#if !CPU(X86_64)
void addl_mr(const void* addr, RegisterID dst)
{
@@ -577,7 +577,7 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void subl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
@@ -671,12 +671,12 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void xorq_rm(RegisterID src, int offset, RegisterID base)
{
m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset);
}
-
+
void rorq_i8r(int imm, RegisterID dst)
{
if (imm == 1)
@@ -749,7 +749,7 @@ public:
{
m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
}
-
+
void shrl_i8r(int imm, RegisterID dst)
{
if (imm == 1)
@@ -759,7 +759,7 @@ public:
m_formatter.immediate8(imm);
}
}
-
+
void shrl_CLr(RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst);
@@ -834,7 +834,7 @@ public:
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
m_formatter.immediate32(imm);
}
-
+
void cmpl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
@@ -845,19 +845,19 @@ public:
m_formatter.immediate32(imm);
}
}
-
+
void cmpb_im(int imm, int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset);
m_formatter.immediate8(imm);
}
-
+
void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset);
m_formatter.immediate8(imm);
}
-
+
#if CPU(X86)
void cmpb_im(int imm, const void* addr)
{
@@ -985,7 +985,7 @@ public:
{
m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
}
-
+
void testl_i32r(int imm, RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst);
@@ -1008,7 +1008,7 @@ public:
m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset);
m_formatter.immediate8(imm);
}
-
+
void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset);
@@ -1057,14 +1057,14 @@ public:
m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
m_formatter.immediate32(imm);
}
-#endif
+#endif
void testw_rr(RegisterID src, RegisterID dst)
{
m_formatter.prefix(PRE_OPERAND_SIZE);
m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
}
-
+
void testb_i8r(int imm, RegisterID dst)
{
m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst);
@@ -1124,7 +1124,7 @@ public:
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, dst);
}
-
+
void movl_rm(RegisterID src, int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
@@ -1139,7 +1139,7 @@ public:
{
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
}
-
+
void movl_mEAX(const void* addr)
{
m_formatter.oneByteOp(OP_MOV_EAXOv);
@@ -1159,7 +1159,7 @@ public:
{
m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset);
}
-
+
void movl_mr_disp8(int offset, RegisterID base, RegisterID dst)
{
m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset);
@@ -1181,7 +1181,7 @@ public:
m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset);
m_formatter.immediate32(imm);
}
-
+
void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset);
@@ -1210,12 +1210,12 @@ public:
m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset);
m_formatter.immediate8(imm);
}
-
+
void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset);
}
-
+
void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_OPERAND_SIZE);
@@ -1296,22 +1296,22 @@ public:
m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
m_formatter.immediate64(imm);
}
-
+
void movsxd_rr(RegisterID src, RegisterID dst)
{
m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src);
}
-
-
+
+
#else
void movl_rm(RegisterID src, const void* addr)
{
if (src == X86Registers::eax)
movl_EAXm(addr);
- else
+ else
m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
}
-
+
void movl_mr(const void* addr, RegisterID dst)
{
if (dst == X86Registers::eax)
@@ -1351,7 +1351,7 @@ public:
{
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
}
-
+
void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
{
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
@@ -1361,7 +1361,7 @@ public:
{
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
}
-
+
void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
{
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
@@ -1404,13 +1404,13 @@ public:
m_formatter.oneByteOp(OP_CALL_rel32);
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel call(RegisterID dst)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst);
return m_formatter.label();
}
-
+
void call_m(int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset);
@@ -1421,7 +1421,7 @@ public:
m_formatter.oneByteOp(OP_JMP_rel32);
return m_formatter.immediateRel32();
}
-
+
// Return a AssemblerLabel so we have a label to the jump, so we can use this
// To make a tail recursive call on x86-64. The MacroAssembler
// really shouldn't wrap this as a Jump, since it can't be linked. :-/
@@ -1430,12 +1430,12 @@ public:
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst);
return m_formatter.label();
}
-
+
void jmp_m(int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset);
}
-
+
#if !CPU(X86_64)
void jmp_m(const void* address)
{
@@ -1448,7 +1448,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionNE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jnz()
{
return jne();
@@ -1459,7 +1459,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jz()
{
return je();
@@ -1470,25 +1470,25 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionL));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jb()
{
m_formatter.twoByteOp(jccRel32(ConditionB));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jle()
{
m_formatter.twoByteOp(jccRel32(ConditionLE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jbe()
{
m_formatter.twoByteOp(jccRel32(ConditionBE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jge()
{
m_formatter.twoByteOp(jccRel32(ConditionGE));
@@ -1506,13 +1506,13 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionA));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jae()
{
m_formatter.twoByteOp(jccRel32(ConditionAE));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel jo()
{
m_formatter.twoByteOp(jccRel32(ConditionO));
@@ -1530,7 +1530,7 @@ public:
m_formatter.twoByteOp(jccRel32(ConditionP));
return m_formatter.immediateRel32();
}
-
+
AssemblerLabel js()
{
m_formatter.twoByteOp(jccRel32(ConditionS));
@@ -1611,7 +1611,7 @@ public:
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src);
}
-
+
#if CPU(X86_64)
void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst)
{
@@ -1657,19 +1657,19 @@ public:
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
}
-
+
void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
}
-
+
void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
}
-
+
void movsd_mr(int offset, RegisterID base, XMMRegisterID dst)
{
m_formatter.prefix(PRE_SSE_F2);
@@ -1681,7 +1681,7 @@ public:
m_formatter.prefix(PRE_SSE_F2);
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset);
}
-
+
void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
{
m_formatter.prefix(PRE_SSE_F3);
@@ -1800,7 +1800,7 @@ public:
{
m_formatter.oneByteOp(OP_INT3);
}
-
+
void ret()
{
m_formatter.oneByteOp(OP_RET);
@@ -1817,7 +1817,7 @@ public:
{
return m_formatter.codeSize();
}
-
+
AssemblerLabel labelForWatchpoint()
{
AssemblerLabel result = m_formatter.label();
@@ -1827,7 +1827,7 @@ public:
m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize();
return result;
}
-
+
AssemblerLabel labelIgnoringWatchpoints()
{
return m_formatter.label();
@@ -1877,7 +1877,7 @@ public:
memcpy(&val, t_ptr, sizeof(T));
return val;
}
-
+
static void linkJump(void* code, AssemblerLabel from, void* to)
{
ASSERT(from.isSet());
@@ -1903,12 +1903,12 @@ public:
{
setRel32(from, to);
}
-
+
static void relinkCall(void* from, void* to)
{
setRel32(from, to);
}
-
+
static void repatchCompact(void* where, int32_t value)
{
ASSERT(value >= std::numeric_limits<int8_t>::min());
@@ -1925,7 +1925,7 @@ public:
{
setPointer(where, value);
}
-
+
static void* readPointer(void* where)
{
return reinterpret_cast<void**>(where)[-1];
@@ -1939,12 +1939,12 @@ public:
ptr[0] = static_cast<uint8_t>(OP_JMP_rel32);
*reinterpret_cast<int32_t*>(ptr + 1) = static_cast<int32_t>(distance);
}
-
+
static ptrdiff_t maxJumpReplacementSize()
{
return 5;
}
-
+
#if CPU(X86_64)
static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst)
{
@@ -1954,7 +1954,7 @@ public:
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
ptr[0] = PRE_REX | (1 << 3) | (dst >> 3);
ptr[1] = OP_MOV_EAXIv | (dst & 7);
-
+
union {
uint64_t asWord;
uint8_t asBytes[8];
@@ -1964,7 +1964,7 @@ public:
ptr[i] = u.asBytes[i - rexBytes - opcodeBytes];
}
#endif
-
+
static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst)
{
const int opcodeBytes = 1;
@@ -1981,7 +1981,7 @@ public:
for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i)
ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes];
}
-
+
static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst)
{
ASSERT_UNUSED(offset, !offset);
@@ -1999,7 +1999,7 @@ public:
for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i)
ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes];
}
-
+
static void replaceWithLoad(void* instructionStart)
{
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
@@ -2017,7 +2017,7 @@ public:
RELEASE_ASSERT_NOT_REACHED();
}
}
-
+
static void replaceWithAddressComputation(void* instructionStart)
{
uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart);
@@ -2035,7 +2035,7 @@ public:
RELEASE_ASSERT_NOT_REACHED();
}
}
-
+
static unsigned getCallReturnOffset(AssemblerLabel call)
{
ASSERT(call.isSet());
@@ -2047,12 +2047,12 @@ public:
ASSERT(label.isSet());
return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + label.m_offset);
}
-
+
static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b)
{
return b.m_offset - a.m_offset;
}
-
+
PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort)
{
return m_formatter.executableCopy(globalData, ownerUID, effort);
@@ -2086,7 +2086,7 @@ private:
T *ptr = &reinterpret_cast<T*>(where)[idx];
memcpy(ptr, &value, sizeof(T));
}
-
+
static void setInt8(void* where, int8_t value)
{
reinterpret_cast<int8_t*>(where)[-1] = value;
@@ -2172,7 +2172,7 @@ private:
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, base, offset);
}
-
+
void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
m_buffer.ensureSpace(maxInstructionSize);
@@ -2286,7 +2286,7 @@ private:
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, base, offset);
}
-
+
void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
m_buffer.ensureSpace(maxInstructionSize);
@@ -2365,7 +2365,7 @@ private:
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm)
{
m_buffer.ensureSpace(maxInstructionSize);
- emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
+ emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
registerModRM(reg, rm);
@@ -2567,7 +2567,7 @@ private:
m_buffer.putIntUnchecked(offset);
}
}
-
+
void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset)
{
ASSERT(index != noIndex);
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index cee6b40599..f7d8a0b30f 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -97,6 +97,9 @@ static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable)
{
#if OS(QNX)
+ UNUSED_PARAM(usage);
+ UNUSED_PARAM(writable);
+ UNUSED_PARAM(executable);
// Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now.
void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
if (result == MAP_FAILED)
diff --git a/src/imports/builtins/CMakeLists.txt b/src/imports/builtins/CMakeLists.txt
index fb6984a961..e1632e1b09 100644
--- a/src/imports/builtins/CMakeLists.txt
+++ b/src/imports/builtins/CMakeLists.txt
@@ -4,3 +4,10 @@ qt_path_join(qml_install_dir "${QT_INSTALL_DIR}" "${INSTALL_QMLDIR}")
qt_copy_or_install(FILES ${qml_type_files}
DESTINATION ${qml_install_dir}
)
+
+# in prefix builds we also need to copy the files into the build directory of
+# the module, so that they are located together with the QML modules
+if(QT_WILL_INSTALL)
+ qt_path_join(qml_build_dir "${QT_BUILD_DIR}" "${INSTALL_QMLDIR}")
+ file(COPY ${qml_type_files} DESTINATION ${qml_build_dir})
+endif()
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index 93f6019901..4be2b6c526 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -23,6 +23,17 @@ Module {
}
Component {
+ name: "QVariantList"
+ valueType: "QVariant"
+ accessSemantics: "sequence"
+ }
+
+ Component {
+ name: "QVariantMap"
+ accessSemantics: "value"
+ }
+
+ Component {
name: "QJSValue"
accessSemantics: "value"
}
@@ -173,6 +184,12 @@ Module {
}
Component {
+ name: "QStringList"
+ valueType: "QString"
+ accessSemantics: "sequence"
+ }
+
+ Component {
name: "bool"
extension: "Boolean"
exports: ["QML/bool 1.0"]
@@ -197,17 +214,6 @@ Module {
}
Component {
- name: "QVariantList"
- valueType: "QVariant"
- accessSemantics: "sequence"
- }
-
- Component {
- name: "QVariantMap"
- accessSemantics: "value"
- }
-
- Component {
name: "QRegularExpression"
exports: ["QML/regexp 1.0"]
extension: "RegExp"
diff --git a/src/labs/models/qqmltablemodel_p.h b/src/labs/models/qqmltablemodel_p.h
index 60897fe007..c865d0b16a 100644
--- a/src/labs/models/qqmltablemodel_p.h
+++ b/src/labs/models/qqmltablemodel_p.h
@@ -55,6 +55,7 @@
#include "qqmltablemodelcolumn_p.h"
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtCore/QAbstractTableModel>
#include <QtQml/qqml.h>
#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
diff --git a/src/labs/models/qqmltablemodelcolumn_p.h b/src/labs/models/qqmltablemodelcolumn_p.h
index 7da31c9e2a..ba625bd656 100644
--- a/src/labs/models/qqmltablemodelcolumn_p.h
+++ b/src/labs/models/qqmltablemodelcolumn_p.h
@@ -54,6 +54,7 @@
#include "qqmlmodelsglobal_p.h"
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtQml/qqml.h>
#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
#include <QtQml/qjsvalue.h>
diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h
index 4e6089c814..6caa719e71 100644
--- a/src/particles/qquickparticleaffector_p.h
+++ b/src/particles/qquickparticleaffector_p.h
@@ -52,6 +52,7 @@
//
#include <QObject>
+#include <QSet>
#include "qquickparticlesystem_p.h"
#include "qquickparticleextruder_p.h"
#include "qtquickparticlesglobal_p.h"
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index 660794d454..b5a7889b7c 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -54,7 +54,9 @@
#include <QtQuick/QQuickItem>
#include <QElapsedTimer>
#include <QVector>
+#include <QVarLengthArray>
#include <QHash>
+#include <QSet>
#include <QPointer>
#include <private/qquicksprite_p.h>
#include <QAbstractAnimation>
diff --git a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
index 0c514ea6c3..7ebbf09dde 100644
--- a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
+++ b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt
@@ -34,6 +34,6 @@ qt_internal_extend_target(QQmlPreviewServiceFactoryPlugin CONDITION QT_FEATURE_t
SOURCES
proxytranslator.cpp proxytranslator.h
qqmldebugtranslationservice.cpp qqmldebugtranslationservice.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
)
diff --git a/src/plugins/scenegraph/openvg/CMakeLists.txt b/src/plugins/scenegraph/openvg/CMakeLists.txt
index da1cb42656..a0999213bd 100644
--- a/src/plugins/scenegraph/openvg/CMakeLists.txt
+++ b/src/plugins/scenegraph/openvg/CMakeLists.txt
@@ -29,7 +29,7 @@ qt_internal_add_plugin(QSGOpenVGAdaptationPlugin
qsgopenvgrenderer.cpp qsgopenvgrenderer_p.h
qsgopenvgrenderloop.cpp qsgopenvgrenderloop_p.h
qsgopenvgtexture.cpp qsgopenvgtexture.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::Gui
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index 2fbf187978..6420411427 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -584,11 +584,6 @@ qt_internal_extend_target(Qml CONDITION hpux-_x_ OR solaris-_x_ OR (QT_FEATURE_c
rt
)
-qt_internal_extend_target(Qml CONDITION QT_FEATURE_qml_itemmodel
- SOURCES
- types/qqmlmodelindexvaluetype.cpp types/qqmlmodelindexvaluetype_p.h
-)
-
qt_internal_extend_target(Qml CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
WTF_USE_UDIS86=1
diff --git a/src/qml/Qt6AndroidQmlMacros.cmake b/src/qml/Qt6AndroidQmlMacros.cmake
index 9ccef4c8e3..df25285462 100644
--- a/src/qml/Qt6AndroidQmlMacros.cmake
+++ b/src/qml/Qt6AndroidQmlMacros.cmake
@@ -46,13 +46,8 @@ function(_qt_internal_generate_android_qml_deployment_settings out_var target)
${target} "_qt_android_native_qml_root_paths")
# Override qmlimportscanner binary path
- set(qml_importscanner_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}/qmlimportscanner")
- if (WIN32)
- string(APPEND qml_importscanner_binary_path ".exe")
- endif()
- file(TO_CMAKE_PATH "${qml_importscanner_binary_path}" qml_importscanner_binary_path_native)
- string(APPEND ${out_var}
- " \"qml-importscanner-binary\" : \"${qml_importscanner_binary_path_native}\",\n")
+ _qt_internal_add_tool_to_android_deployment_settings(${out_var} qmlimportscanner
+ "qml-importscanner-binary" ${target})
set(${out_var} "${${out_var}}" PARENT_SCOPE)
endfunction()
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index 87daf8688f..ad42596931 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -36,6 +36,7 @@ macro(qt_internal_get_internal_add_qml_module_keywords
IMPORTS
IMPORT_PATH
OPTIONAL_IMPORTS
+ DEFAULT_IMPORTS
DEPENDENCIES
PAST_MAJOR_VERSIONS
)
diff --git a/src/qml/Qt6QmlDeploySupport.cmake b/src/qml/Qt6QmlDeploySupport.cmake
index 58ca93b5fa..dc16261170 100644
--- a/src/qml/Qt6QmlDeploySupport.cmake
+++ b/src/qml/Qt6QmlDeploySupport.cmake
@@ -50,7 +50,9 @@ function(qt_deploy_qml_imports)
message(FATAL_ERROR
"No QML imports information recorded for target ${arg_TARGET}. "
"The target must be an executable and qt_add_qml_module() must "
- "have been called with it. Missing file:\n ${filename}"
+ "have been called with it. If using a CMake version lower than 3.19, ensure "
+ "that the executable is manually finalized with qt_finalize_target(). "
+ "Missing file:\n ${filename}"
)
endif()
include(${filename})
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 769eb87093..93ce173646 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -66,6 +66,7 @@ function(qt6_add_qml_module target)
IMPORTS
IMPORT_PATH
OPTIONAL_IMPORTS
+ DEFAULT_IMPORTS
DEPENDENCIES
PAST_MAJOR_VERSIONS
)
@@ -382,7 +383,7 @@ function(qt6_add_qml_module target)
set(arg_TYPEINFO ${target}.qmltypes)
endif()
- foreach(import_set IN ITEMS IMPORTS OPTIONAL_IMPORTS)
+ foreach(import_set IN ITEMS IMPORTS OPTIONAL_IMPORTS DEFAULT_IMPORTS)
foreach(import IN LISTS arg_${import_set})
string(FIND ${import} "/" slash_position REVERSE)
if (slash_position EQUAL -1)
@@ -448,7 +449,7 @@ function(qt6_add_qml_module target)
QT_QML_MODULE_NO_PLUGIN "${arg_NO_PLUGIN}"
QT_QML_MODULE_NO_PLUGIN_OPTIONAL "${arg_NO_PLUGIN_OPTIONAL}"
QT_QML_MODULE_NO_IMPORT_SCAN "${arg_NO_IMPORT_SCAN}"
- QT_QML_MODULE_FOLLOW_FOREIGN_VERSIONING "${arg_FOLLOW_FOREIGN_VERSIONING}"
+ _qt_qml_module_follow_foreign_versioning "${arg_FOLLOW_FOREIGN_VERSIONING}"
QT_QML_MODULE_URI "${arg_URI}"
QT_QML_MODULE_TARGET_PATH "${arg_TARGET_PATH}"
QT_QML_MODULE_VERSION "${arg_VERSION}"
@@ -689,6 +690,20 @@ macro(_qt_internal_genex_getoption var target property)
set(${var} "$<BOOL:$<TARGET_PROPERTY:${target},${property}>>")
endmacro()
+function(_qt_internal_extend_qml_import_paths import_paths_var)
+ set(local_var ${${import_paths_var}})
+
+ # prepend extra import path which is a current module's build dir: we need
+ # this to ensure correct importing of QML modules when having a prefix-build
+ # with QLibraryInfo::path(QLibraryInfo::QmlImportsPath) pointing to the
+ # install location
+ if(QT_BUILDING_QT AND QT_WILL_INSTALL)
+ list(PREPEND local_var -I "${QT_BUILD_DIR}/${INSTALL_QMLDIR}")
+ endif()
+
+ set(${import_paths_var} ${local_var} PARENT_SCOPE)
+endfunction()
+
function(_qt_internal_target_enable_qmllint target)
set(lint_target ${target}_qmllint)
if(TARGET ${lint_target})
@@ -737,6 +752,8 @@ function(_qt_internal_target_enable_qmllint target)
list(APPEND import_args -I "${QT_QML_OUTPUT_DIRECTORY}")
endif()
+ _qt_internal_extend_qml_import_paths(import_args)
+
set(cmd
${QT_TOOL_COMMAND_WRAPPER_PATH}
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
@@ -986,6 +1003,8 @@ function(_qt_internal_target_generate_qmldir target)
_qt_internal_qmldir_item_list(import QT_QML_MODULE_IMPORTS)
_qt_internal_qmldir_item_list("optional import" QT_QML_MODULE_OPTIONAL_IMPORTS)
+ _qt_internal_qmldir_item_list("default import" QT_QML_MODULE_DEFAULT_IMPORTS)
+
_qt_internal_qmldir_item_list(depends QT_QML_MODULE_DEPENDENCIES)
get_target_property(prefix ${target} QT_QML_MODULE_RESOURCE_PREFIX)
@@ -1109,6 +1128,8 @@ function(qt6_target_compile_qml_to_cpp target)
list(APPEND common_args -I "${import_path}")
endforeach()
+ _qt_internal_extend_qml_import_paths(common_args)
+
# we explicitly depend on qmldir (due to `-i ${qmldir_file}`) but also
# implicitly on the generated qmltypes file, which is a part of qmldir
set(qml_module_files)
@@ -1587,7 +1608,6 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
-
function(qt6_target_qml_sources target)
get_target_property(uri ${target} QT_QML_MODULE_URI)
@@ -1721,6 +1741,7 @@ function(qt6_target_qml_sources target)
# The application binary directory is part of the default import path.
list(APPEND import_paths -I "$<TARGET_PROPERTY:${target},BINARY_DIR>")
endif()
+ _qt_internal_extend_qml_import_paths(import_paths)
set(cachegen_args
${import_paths}
-i "${qmldir_file}"
@@ -1841,7 +1862,7 @@ function(qt6_target_qml_sources target)
# The set of qml files to run qmllint on may be a subset of the
# full set of files, so record these in a separate property.
_qt_internal_target_enable_qmllint(${target})
- set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${qml_file_src})
+ set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${file_absolute})
endif()
# Add qml file's type to qmldir
@@ -2061,22 +2082,24 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
-function(qt6_generate_foreign_qml_types lib_target qml_target)
-
- qt6_extract_metatypes(${lib_target})
- get_target_property(target_metatypes_json_file ${lib_target} INTERFACE_QT_META_TYPES_BUILD_FILE)
+# This function is currently in Technical Preview.
+# It's signature and behavior might change.
+function(qt6_generate_foreign_qml_types source_target destination_qml_target)
+ qt6_extract_metatypes(${source_target})
+ get_target_property(target_metatypes_json_file ${source_target}
+ INTERFACE_QT_META_TYPES_BUILD_FILE)
if (NOT target_metatypes_json_file)
message(FATAL_ERROR "Need target metatypes.json file")
endif()
- set(registration_files_base ${lib_target}_${qml_target})
+ set(registration_files_base ${source_target}_${destination_qml_target})
set(additional_sources ${registration_files_base}.cpp ${registration_files_base}.h)
add_custom_command(
OUTPUT
${additional_sources}
DEPENDS
- ${target}
+ ${source_target}
${target_metatypes_json_file}
${QT_CMAKE_EXPORT_NAMESPACE}::qmltyperegistrar
COMMAND
@@ -2085,14 +2108,13 @@ function(qt6_generate_foreign_qml_types lib_target qml_target)
"--extract"
-o ${registration_files_base}
${target_metatypes_json_file}
- COMMENT "Generate QML registration code for target ${target}"
+ COMMENT "Generate QML registration code for target ${source_target}"
)
- target_sources(${qml_target} PRIVATE ${additional_sources})
- qt6_wrap_cpp(${additional_sources} TARGET ${qml_target})
+ target_sources(${destination_qml_target} PRIVATE ${additional_sources})
+ qt6_wrap_cpp(${additional_sources} TARGET ${destination_qml_target})
endfunction()
-
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
function(qt_generate_foreign_qml_types)
@@ -2202,7 +2224,8 @@ function(_qt_internal_qml_type_registration target)
# Add --follow-foreign-versioning if requested
- get_target_property(follow_foreign_versioning ${target} QT_QML_MODULE_FOLLOW_FOREIGN_VERSIONING)
+ get_target_property(follow_foreign_versioning ${target}
+ _qt_qml_module_follow_foreign_versioning)
if (follow_foreign_versioning)
list(APPEND cmd_args
@@ -2313,10 +2336,12 @@ function(_qt_internal_qml_type_registration target)
endif()
add_dependencies(all_qmltyperegistrations ${target}_qmltyperegistration)
+ # Both ${target} (via target_sources) and ${target}_qmltyperegistration (via add_custom_target
+ # DEPENDS option) depend on ${type_registration_cpp_file}.
# The new Xcode build system requires a common target to drive the generation of files,
# otherwise project configuration fails.
- # Make the ${target}_qmltyperegistration the common target, by adding it as a dependency for
- # ${target} itself.
+ # Make ${target} the common target, by adding it as a dependency for
+ # ${target}_qmltyperegistration.
# The consequence is that the ${target}_qmllint target will now first build ${target} when using
# the Xcode generator (mostly only relevant for projects using Qt for iOS).
# See QTBUG-95763.
@@ -2721,6 +2746,22 @@ function(qt6_generate_deploy_qml_app_script)
message(FATAL_ERROR "FILENAME_VARIABLE must be specified")
endif()
+ # Check that the target was defer-finalized, and not immediately finalized when using
+ # CMake < 3.19. This is important because if it's immediately finalized, Qt::Qml is likely
+ # not in the dependency list, and thus _qt_internal_generate_deploy_qml_imports_script will
+ # not be executed, leading to an error at install time
+ # 'No QML imports information recorded for target X'.
+ # _qt_is_immediately_finalized is set by qt6_add_executable.
+ # TODO: Remove once minimum required CMAKE_VERSION is 3.19+.
+ get_target_property(is_immediately_finalized "${arg_TARGET}" _qt_is_immediately_finalized)
+ if(is_immediately_finalized)
+ message(FATAL_ERROR
+ "QML app deployment requires CMake version 3.19, or later, or manual executable "
+ "finalization. For manual finalization, pass the MANUAL_FINALIZATION option to "
+ "qt_add_executable() and then call qt_finalize_target(${arg_TARGET}) just before
+ calling qt_generate_deploy_qml_app_script().")
+ endif()
+
# Create a file name that will be unique for this target and the combination
# of arguments passed to this command. This allows the project to call us
# multiple times with different arguments for the same target (e.g. to
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 146588b750..2ed7c24efe 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -67,7 +67,6 @@
#include <private/qendian_p.h>
#include <private/qv4staticvalue_p.h>
-#include <private/qml_compile_hash_p.h>
#include <functional>
#include <limits.h>
@@ -945,7 +944,6 @@ struct QmlUnit
static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
enum { QmlCompileHashSpace = 48 };
-static_assert(QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
static const char magic_str[] = "qv4cdata";
struct Unit
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 59998e531c..4813e2203c 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -56,6 +56,9 @@
#include <QtCore/QDateTime>
#include <QtCore/QStack>
#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QSet>
+#include <QtCore/QVarLengthArray>
#include <memory>
diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h
index d0ceb28cdc..d2a0570423 100644
--- a/src/qml/debugger/qqmldebug.h
+++ b/src/qml/debugger/qqmldebug.h
@@ -43,6 +43,7 @@
#include <QtQml/qtqmlglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
+#include <QtCore/qhash.h> // QVariantHash
QT_BEGIN_NAMESPACE
diff --git a/src/qml/doc/snippets/code/doc_src_qtqml.cmake b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
index c312201ff8..30ff8981a9 100644
--- a/src/qml/doc/snippets/code/doc_src_qtqml.cmake
+++ b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
@@ -2,3 +2,9 @@
find_package(Qt6 REQUIRED COMPONENTS Qml)
target_link_libraries(mytarget PRIVATE Qt6::Qml)
#! [0]
+
+#! [1]
+find_package(Qt6 REQUIRED COMPONENTS QmlIntegration)
+target_link_libraries(mytarget PRIVATE Qt6::QmlIntegration)
+#! [1]
+
diff --git a/src/qml/doc/snippets/qmltc/CMakeLists.txt b/src/qml/doc/snippets/qmltc/CMakeLists.txt
index 69bfda7249..671db0e30d 100644
--- a/src/qml/doc/snippets/qmltc/CMakeLists.txt
+++ b/src/qml/doc/snippets/qmltc/CMakeLists.txt
@@ -52,8 +52,8 @@ qt6_add_qml_module(${application_name}
# (qmltc-specific) Link *private* libraries that correspond to QML modules:
target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivate)
-# Compile qml files (listed in FILES) to C++ using qmltc and add these files to
-# the application binary:
+# Compile qml files (listed in QML_FILES) to C++ using qmltc and add these files
+# to the application binary:
qt6_target_compile_qml_to_cpp(${application_name}
QML_FILES ${application_qml_files}
)
diff --git a/src/qml/doc/snippets/qmltc/myApp.qml b/src/qml/doc/snippets/qmltc/myApp.qml
index 234fa39ab9..80d94829bf 100644
--- a/src/qml/doc/snippets/qmltc/myApp.qml
+++ b/src/qml/doc/snippets/qmltc/myApp.qml
@@ -91,6 +91,7 @@ Rectangle {
id: rndText
font.pixelSize: 25
color: textColor
+ text: "0.00"
}
Rectangle {
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
index ddbf3baaaa..122084f1d8 100644
--- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
@@ -71,15 +71,15 @@ class HelloWorld : public QObject
public:
HelloWorld(QQmlEngine * engine, QObject * parent = nullptr);
+Q_SIGNALS:
+ void created();
+
public:
void setHello(const QString& hello_);
QString hello();
QBindable<QString> bindableHello();
Q_INVOKABLE void printHello(QString prefix, QString suffix);
-signals:
- void created();
-
// ...
};
//! [qmltc-hello-world-generated]
diff --git a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
index 50436dc26f..2d619ebeea 100644
--- a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
+++ b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
@@ -177,6 +177,9 @@ void readFileContent(QStringList *content, const QString &url, Predicate filter)
void tst_qmltc_examples::helloWorld()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("expected C++ files are not bundled with Android tests.");
+#endif
QStringList generatedCode;
readFileContent(&generatedCode,
QStringLiteral(QMLTC_TESTS_BINARY_DIR)
diff --git a/src/qml/doc/src/cmake/cmake-properties.qdoc b/src/qml/doc/src/cmake/cmake-properties.qdoc
new file mode 100644
index 0000000000..eccc815eca
--- /dev/null
+++ b/src/qml/doc/src/cmake/cmake-properties.qdoc
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\group cmake-source-file-properties-qtqml
+\title CMake Source File Properties in Qt6 Qml
+
+\l{CMake Commands in Qt6 Qml}{CMake Commands} know about the following CMake
+source file properties:
+
+\sa{CMake Property Reference}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_INTERNAL_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_INTERNAL_TYPE
+\target cmake-source-file-property-QT_QML_INTERNAL_TYPE
+
+\summary {Marks a QML file as providing an internal type.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to indicate that the \c{.qml} file provides an internal type.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SINGLETON_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SINGLETON_TYPE
+\target cmake-source-file-property-QT_QML_SINGLETON_TYPE
+
+\summary {Marks a QML file as providing a singleton type.}
+
+\cmakepropertysince 6.2
+
+A \c{.qml} file that provides a singleton type needs to have its \c QT_QML_SINGLETON_TYPE source
+property set to \c TRUE to ensure that the singleton command is written into the
+\l{Module Definition qmldir Files}{qmldir} file.
+This must be done in addition to the QML file containing the \c {pragma Singleton} statement.
+
+See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
+how to set the \c QT_QML_SINGLETON_TYPE property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_CACHEGEN.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_CACHEGEN
+\target cmake-source-file-property-QT_QML_SKIP_CACHEGEN
+
+\summary {Excludes a file from being compiled to byte code.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the \c{.qml} file from being compiled to byte code.
+The file will still be added to the \c target as a resource in uncompiled form
+(see \l{qmlcachegen-auto}{Caching compiled QML sources}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLDIR_ENTRY
+\target cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY
+
+\summary {Excludes a file from being added as a type to the QML module's typeinfo file.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent
+the \c{.qml} file from being added as a type to the QML module's typeinfo file
+(see \l{qmldir-autogeneration}{Auto-generating \c{qmldir} and typeinfo files}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLLINT.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLLINT
+\target cmake-source-file-property-QT_QML_SKIP_QMLLINT
+
+\summary {Prevents a file from being included in automatic qmllint processing.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the file from being included in
+\l{qmllint-auto}{automatic qmllint processing}.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_TYPENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_TYPENAME
+\target cmake-source-file-property-QT_QML_SOURCE_TYPENAME
+
+\summary {Overrides the type name provided by the file.}
+
+\cmakepropertysince 6.2
+
+Use this property to override the \c QML type name provided by this file.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_VERSIONS.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_VERSIONS
+\target cmake-source-file-property-QT_QML_SOURCE_VERSIONS
+
+\summary {Specifies a custom set of versions for a type.}
+
+\cmakepropertysince 6.2
+
+When the file needs to provide type entries for a custom set of versions,
+for example when the QML types were first introduced in a minor patch
+version after the \c{.0} release, specify those versions using this property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QMLTC_FILE_BASENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QMLTC_FILE_BASENAME
+\target cmake-source-file-property-QT_QMLTC_FILE_BASENAME
+
+\summary {Specifies a non-default .h and .cpp file name.}
+
+\cmakepropertysince 6.3
+\preliminarycmakeproperty
+
+Use this property to specify a non-default \c .h and \c .cpp file name, which helps to resolve
+conflicting file names.
+
+\sa{qt_target_compile_qml_to_cpp}
+*/
diff --git a/src/qml/doc/src/cmake/cmake-variables.qdoc b/src/qml/doc/src/cmake/cmake-variables.qdoc
index 8f5b87b1cb..df01534d66 100644
--- a/src/qml/doc/src/cmake/cmake-variables.qdoc
+++ b/src/qml/doc/src/cmake/cmake-variables.qdoc
@@ -27,7 +27,6 @@
/*!
\page cmake-variable-QT_QML_OUTPUT_DIRECTORY.html
-\ingroup cmake-variables
\ingroup cmake-variables-qtqml
\title QT_QML_OUTPUT_DIRECTORY
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index d357497e24..00381d086d 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -34,6 +34,8 @@
\brief Defines a QML module.
+\cmakecommandsince 6.2
+
\section1 Synopsis
\badcode
@@ -50,6 +52,7 @@ qt_add_qml_module(
[TYPEINFO typeinfo]
[IMPORTS ...]
[OPTIONAL_IMPORTS ...]
+ [DEFAULT_IMPORTS ...]
[DEPENDENCIES ...]
[IMPORT_PATH ...]
[SOURCES ...]
@@ -467,6 +470,15 @@ like \c qmllint. Versions can be specified in the same way as for \c IMPORTS.
Each module listed here will be added as an \c{optional import} entry in the
generated \l{Module Definition qmldir Files}{qmldir} file.
+\c DEFAULT_IMPORTS specifies which of the optional imports are the default entries
+that should be loaded by tooling. One entry should be specified for every group of
+\c OPTIONAL_IMPORTS in the module. As optional imports are only resolved at runtime,
+tooling like qmllint cannot in general know which of the optional imports should
+be resolved. To remedy this, you can specify one of the optional imports as the
+default import; tooling will then pick it. If you have one optional import that
+gets used at runtime without any further configuration, that is an ideal candidate
+for the default import.
+
\c DEPENDENCIES provides a list of other QML modules that this module depends
on, but doesn't necessarily import. It would typically be used for dependencies
that only exist at the C++ level, such as a module registering a class to QML
diff --git a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
index 0282e2347e..369e698c39 100644
--- a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
+++ b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
@@ -42,6 +42,8 @@ project.
\preliminarycmakecommand
+\include cmake-qml-qt-finalize-target-warning.cmake
+
\section1 Synopsis
\badcode
@@ -97,7 +99,7 @@ named by the \c{PLUGINS_FOUND} option, if given. This is often passed as the
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
qt_standard_project_setup()
qt_add_executable(MyApp main.cpp)
@@ -113,7 +115,23 @@ set(deploy_script "${CMAKE_CURRENT_BINARY_DIR}/deploy_MyApp.cmake")
file(GENERATE OUTPUT ${deploy_script} CONTENTS "
include(\"${QT_DEPLOY_SUPPORT}\")
qt_deploy_qml_imports(
- EXECUTABLE \"\${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:MyApp>\"
+ # Deploy QML modules used by MyApp
+ TARGET MyApp
+
+ # The found QML plugins are stored in the plugins_found variable
+ PLUGINS_FOUND plugins_found
+
+ # The QML modules will be deployed into a custom directory
+ QML_DIR \"myqmldir\"
+
+ # Qt QML modules will be skipped, only project-created QML modules will be deployed
+ NO_QT_IMPORTS
+)
+# Deploy application runtime dependencies and runtime dependencies
+# of the found QML module plugins.
+qt_deploy_runtime_dependencies(
+ EXECUTABLE \${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:MyApp>
+ ADDITIONAL_MODULES \${plugins_found}
)
")
diff --git a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
index 1fae4ef9d1..360d602f8c 100644
--- a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
@@ -36,8 +36,11 @@
\include cmake-find-package-qml.qdocinc
+\cmakecommandsince 6.3
\preliminarycmakecommand
+\include cmake-qml-qt-finalize-target-warning.cmake
+
\section1 Synopsis
\badcode
@@ -122,7 +125,7 @@ same target.
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
qt_standard_project_setup()
qt_add_executable(MyApp main.cpp)
diff --git a/src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
index 07cdd92543..8f1d6522be 100644
--- a/src/qml/doc/src/cmake/qt6_generate_foreign_qml_types.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
@@ -26,20 +26,24 @@
****************************************************************************/
/*!
-\page qt6_generate_foreign_qml_types.html
+\page qt_generate_foreign_qml_types.html
\ingroup cmake-commands-qtqml
-\title qt6_generate_foreign_qml_types
+\title qt_generate_foreign_qml_types
\target qt6_generate_foreign_qml_types
\summary{Registers types from one target in a QML module.}
+\include cmake-find-package-qml.qdocinc
+
+\preliminarycmakecommand
+
\section1 Synopsis
\badcode
-qt_add_qml_module(
- foreign_target
- qml_lib_target
+qt_generate_foreign_qml_types(
+ source_target
+ destination_qml_target
)
\endcode
@@ -48,8 +52,9 @@ qt_add_qml_module(
\section1 Description
-\c qt6_generate_foreign_qml_types enables the registration of types marked via QML registration
-macros (like \l QML_ELEMENT) in \c foreign_lib in the QML module \c qml_lib_target.
+\c qt_generate_foreign_qml_types extracts types marked via QML registration
+macros (like \l QML_ELEMENT) from \c source_target and registers them as foreign
+types in the QML module \c destination_qml_target.
This can be useful when one wants to create a library with optional QML integration, without
depending directly on QML.
@@ -70,14 +75,14 @@ class MyClass : public QObject
\badcode
# CMakeLists.txt
qt_add_library(mylib myclass.h ...)
-target_link_libraries(mylib Qt::Core Qt::QmlIntegration)
+target_link_libraries(mylib PRIVATE Qt::Core Qt::QmlIntegration)
qt_add_qml_module(mylib_declarative
VERSION 1.0
URI "mylib"
...
)
-qt6_generate_foreign_qml_types(mylib mylib_declarative)
+qt_generate_foreign_qml_types(mylib mylib_declarative)
\endcode
\note In the example above, \c mylib does not depend on QtQml or QtQuick, but only on the
diff --git a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
index 639c7ad223..b720d6d592 100644
--- a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
+++ b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
@@ -34,6 +34,8 @@
\brief Ensures QML plugins needed by a target are imported for static builds.
+\cmakecommandsince 6.0
+
\section1 Synopsis
\badcode
diff --git a/src/qml/doc/src/cmake/qt_query_qml_module.qdoc b/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
index b2ab6c7c86..ec017ba3e7 100644
--- a/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_query_qml_module.qdoc
@@ -36,6 +36,7 @@
\include cmake-find-package-qml.qdocinc
+\cmakecommandsince 6.3
\preliminarycmakecommand
\section1 Synopsis
@@ -161,7 +162,7 @@ absolute. The meaning and usage of \c RESOURCES_DEPLOY_PATHS and
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+find_package(Qt6 6.3 REQUIRED COMPONENTS Core Qml)
set(module_name "MyThings")
qt_add_qml_module(${module_name}
diff --git a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
index c5a7212caa..dc1178be29 100644
--- a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
+++ b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
@@ -34,6 +34,8 @@
\brief Add qml files and resources to an existing QML module target.
+\cmakecommandsince 6.2
+
\section1 Synopsis
\badcode
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index ad0d2243ec..fd29e733e0 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -271,9 +271,9 @@ Notice the parameter and return type specified after the colon. You can use \l
{QML Value Types}{value types} and \l {QML Object Types}{object types} as type
names.
-If the type is omitted in QML, then you must specify QVariant as type with
-Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod.
-
+If the type is omitted or specified as \c var in QML, then you must pass
+QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
+QMetaObject::invokeMethod.
\section2 Connecting to QML Signals
diff --git a/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake
new file mode 100644
index 0000000000..d3c7383d2e
--- /dev/null
+++ b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.cmake
@@ -0,0 +1,4 @@
+\warning If you are using a CMake version lower than 3.19, make sure that you
+pass the \c MANUAL_FINALIZATION option to
+\l{qt_add_executable}{qt6_add_executable()}, and then call
+\l{qt_finalize_target}{qt6_finalize_target()} before calling this function.
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
index 53fe1c2875..375b8758d5 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
@@ -136,7 +136,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype int
+ \qmlvaluetype int
\ingroup qmlvaluetypes
\brief a whole number, e.g. 0, 10, or -20.
@@ -157,7 +157,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype bool
+ \qmlvaluetype bool
\ingroup qmlvaluetypes
\brief a binary true/false value.
@@ -177,7 +177,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype real
+ \qmlvaluetype real
\ingroup qmlvaluetypes
\brief a number with a decimal point.
@@ -199,7 +199,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype double
+ \qmlvaluetype double
\ingroup qmlvaluetypes
\brief a number with a decimal point, stored in double precision.
@@ -220,7 +220,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype string
+ \qmlvaluetype string
\ingroup qmlvaluetypes
\brief a free form text string.
@@ -247,7 +247,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype url
+ \qmlvaluetype url
\ingroup qmlvaluetypes
\brief a resource locator.
@@ -303,7 +303,7 @@ property is only invoked when the property is reassigned to a different object v
/*!
- \qmlbasictype list
+ \qmlvaluetype list
\ingroup qmlvaluetypes
\brief a list of QML objects.
@@ -385,7 +385,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype var
+ \qmlvaluetype var
\ingroup qmlvaluetypes
\brief a generic property type.
@@ -485,7 +485,7 @@ property is only invoked when the property is reassigned to a different object v
*/
/*!
- \qmlbasictype enumeration
+ \qmlvaluetype enumeration
\ingroup qmlvaluetypes
\brief a named enumeration value.
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc
index 3efcc7cf53..b8decf6820 100644
--- a/src/qml/doc/src/qmltypereference.qdoc
+++ b/src/qml/doc/src/qmltypereference.qdoc
@@ -83,7 +83,7 @@ provided:
*/
/*!
-\qmlbasictype date
+\qmlvaluetype date
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a date value.
@@ -123,7 +123,7 @@ to a \l{QtQml::Date}{Date} object.
*/
/*!
-\qmlbasictype point
+\qmlvaluetype point
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with x and y attributes.
@@ -151,7 +151,7 @@ is automatically converted into a QPointF value.
*/
/*!
-\qmlbasictype size
+\qmlvaluetype size
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with width and height attributes
@@ -189,7 +189,7 @@ is automatically converted into a QSizeF value.
*/
/*!
-\qmlbasictype rect
+\qmlvaluetype rect
\ingroup qtqmlvaluetypes
\ingroup qtquickvaluetypes
\brief a value with x, y, width and height attributes.
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index 4de3115511..de4fa16e29 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -69,6 +69,13 @@ Use the \c find_package() command to locate the needed module components in the
\snippet code/doc_src_qtqml.cmake 0
+To provide foreign QML type support for a non-QML library, locate
+the \c QmlIntegration module as follows:
+
+\snippet code/doc_src_qtqml.cmake 1
+
+See \l{qt6_generate_foreign_qml_types} for details.
+
See also the \l{Build with CMake} overview.
\section3 Building with qmake
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 35b1f18344..93b1e88a1b 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -55,6 +55,7 @@
#include <private/qv4function_p.h>
#include <private/qv4instr_moth_p.h>
#include <private/qv4bytecodehandler_p.h>
+#include <QtCore/qset.h>
#if QT_CONFIG(qml_jit)
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 7b436645e9..f66488e04c 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -861,73 +861,87 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
return convertV2(value, QMetaType(type), ptr);
}
+static bool convertString(const QString &string, QMetaType metaType, void *ptr)
+{
+ // have a string based value without engine. Do conversion manually
+ if (metaType == QMetaType::fromType<bool>()) {
+ *reinterpret_cast<bool*>(ptr) = string.length() != 0;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QString>()) {
+ *reinterpret_cast<QString*>(ptr) = string;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QUrl>()) {
+ *reinterpret_cast<QUrl *>(ptr) = QUrl(string);
+ return true;
+ }
+
+ double d = QV4::RuntimeHelpers::stringToNumber(string);
+ switch (metaType.id()) {
+ case QMetaType::Int:
+ *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UInt:
+ *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::LongLong:
+ *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::ULongLong:
+ *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::Double:
+ *reinterpret_cast<double*>(ptr) = d;
+ return true;
+ case QMetaType::Float:
+ *reinterpret_cast<float*>(ptr) = d;
+ return true;
+ case QMetaType::Short:
+ *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UShort:
+ *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::Char:
+ *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UChar:
+ *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::QChar:
+ *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
+ return true;
+ case QMetaType::Char16:
+ *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ default:
+ return false;
+ }
+}
+
/*!
\internal
convert \a value to \a type, store the result in \a ptr
*/
bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
{
- if (const QString *string = QJSValuePrivate::asQString(&value)) {
- // have a string based value without engine. Do conversion manually
- if (metaType == QMetaType::fromType<bool>()) {
- *reinterpret_cast<bool*>(ptr) = string->length() != 0;
- return true;
- }
- if (metaType == QMetaType::fromType<QString>()) {
- *reinterpret_cast<QString*>(ptr) = *string;
- return true;
- }
- if (metaType == QMetaType::fromType<QUrl>()) {
- *reinterpret_cast<QUrl *>(ptr) = QUrl(*string);
- return true;
- }
-
- double d = QV4::RuntimeHelpers::stringToNumber(*string);
- switch (metaType.id()) {
- case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::Double:
- *reinterpret_cast<double*>(ptr) = d;
- return true;
- case QMetaType::Float:
- *reinterpret_cast<float*>(ptr) = d;
- return true;
- case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
- return true;
- case QMetaType::Char16:
- *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
- return true;
- default:
- return false;
- }
- }
+ if (const QString *string = QJSValuePrivate::asQString(&value))
+ return convertString(*string, metaType, ptr);
return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr);
}
+bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr)
+{
+ if (value.metaType() == QMetaType::fromType<QString>())
+ return convertString(value.toString(), metaType, ptr);
+
+ // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
+ // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
+ return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromVariant(value), metaType, ptr);
+}
+
/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
Creates a QJSValue with the given \a value.
@@ -944,6 +958,18 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
\sa toScriptValue()
*/
+/*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value)
+
+ Returns the given \a value converted to the template type \c{T}.
+ This works with any type \c{T} that has a \c{QMetaType}. The
+ conversion is done in JavaScript semantics. Those differ from
+ qvariant_cast's semantics. There are a number of implicit
+ conversions between JavaScript-equivalent types that are not
+ performed by qvariant_cast by default.
+
+ \sa fromScriptValue() qvariant_cast()
+*/
+
/*!
Throws a run-time error (exception) with the given \a message.
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 23578a63b6..63d6a6dd32 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -113,6 +113,33 @@ public:
return qjsvalue_cast<T>(value);
}
+ template <typename T>
+ inline T fromVariant(const QVariant &value)
+ {
+ if constexpr (std::is_same_v<T, QVariant>)
+ return value;
+
+ const QMetaType targetType = QMetaType::fromType<T>();
+ if (value.metaType() == targetType)
+ return *reinterpret_cast<const T *>(value.constData());
+
+ if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) {
+ using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
+ const QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
+ if (value.metaType() == nonConstTargetType)
+ return *reinterpret_cast<const nonConstT *>(value.constData());
+ }
+
+ {
+ T t{};
+ if (convertVariant(value, targetType, &t))
+ return t;
+
+ QMetaType::convert(value.metaType(), value.constData(), targetType, &t);
+ return t;
+ }
+ }
+
void collectGarbage();
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
@@ -157,6 +184,7 @@ private:
static bool convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr);
static bool convertV2(const QJSValue &value, int type, void *ptr);
static bool convertV2(const QJSValue &value, QMetaType metaType, void *ptr);
+ bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr);
template<typename T>
friend inline T qjsvalue_cast(const QJSValue &);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index aed640281f..033331e08e 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -147,14 +147,23 @@ DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
using namespace QV4;
+// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1
+// initializes the statics and sets engineSerial to 2 afterwards.
+// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays
+// odd while the statics are being initialized, and stays even afterwards.
+// Any further engines created while the statics are being initialized busy-wait until engineSerial
+// is even.
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+int ExecutionEngine::s_maxCallDepth = -1;
+int ExecutionEngine::s_jitCallCountThreshold = 3;
+int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
+int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
{
return b->engine()->throwTypeError();
}
-qint32 ExecutionEngine::maxCallDepth = -1;
template <typename ReturnType>
ReturnType convertJSValueToVariantType(const QJSValue &value)
@@ -324,6 +333,59 @@ static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
return QSequentialIterable(QMetaSequence(&sequence), &value);
}
+void ExecutionEngine::initializeStaticMembers()
+{
+ bool ok = false;
+
+ const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxJSStackSize > 0)
+ s_maxJSStackSize = envMaxJSStackSize;
+
+ const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxGCStackSize > 0)
+ s_maxGCStackSize = envMaxGCStackSize;
+
+ if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
+ s_maxCallDepth = std::numeric_limits<qint32>::max();
+ } else {
+ ok = false;
+ s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
+ if (!ok || s_maxCallDepth <= 0) {
+#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
+#ifdef Q_OS_QNX
+ s_maxCallDepth = 640; // QNX's stack is only 512k by default
+#else
+ s_maxCallDepth = 1234;
+#endif
+#else
+ // no (tail call) optimization is done, so there'll be a lot mare stack frames active
+ s_maxCallDepth = 200;
+#endif
+ }
+ }
+
+ Q_ASSERT(s_maxCallDepth > 0);
+
+ ok = false;
+ s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
+ if (!ok)
+ s_jitCallCountThreshold = 3;
+ if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
+ s_jitCallCountThreshold = std::numeric_limits<int>::max();
+
+ qMetaTypeId<QJSValue>();
+ qMetaTypeId<QList<int> >();
+
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
+ QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
+ QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
+ QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
+ QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -332,7 +394,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, publicEngine(jsEngine)
- , m_engineId(engineSerial.fetchAndAddOrdered(1))
+ , m_engineId(engineSerial.fetchAndAddOrdered(2))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
#if QT_CONFIG(qml_jit)
@@ -343,45 +405,23 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
#endif
, m_qmlEngine(nullptr)
{
- bool ok = false;
- const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
- if (ok && envMaxJSStackSize > 0)
- m_maxJSStackSize = envMaxJSStackSize;
-
- const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
- if (ok && envMaxGCStackSize > 0)
- m_maxGCStackSize = envMaxGCStackSize;
-
- memoryManager = new QV4::MemoryManager(this);
-
- if (maxCallDepth == -1) {
- if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
- maxCallDepth = std::numeric_limits<qint32>::max();
- } else {
- ok = false;
- maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
- if (!ok || maxCallDepth <= 0) {
-#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
-#ifdef Q_OS_QNX
- maxCallDepth = 640; // QNX's stack is only 512k by default
-#else
- maxCallDepth = 1234;
-#endif
-#else
- // no (tail call) optimization is done, so there'll be a lot mare stack frames active
- maxCallDepth = 200;
-#endif
- }
+ if (m_engineId == 1) {
+ initializeStaticMembers();
+ engineSerial.storeRelease(2); // make it even
+ } else if (Q_UNLIKELY(m_engineId & 1)) {
+ // This should be rare. You usually don't create lots of engines at the same time.
+ while (engineSerial.loadAcquire() & 1) {
+ QThread::yieldCurrentThread();
}
}
- Q_ASSERT(maxCallDepth > 0);
+ memoryManager = new QV4::MemoryManager(this);
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
- *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
jsStackBase = (Value *)jsStack->base();
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
@@ -389,19 +429,10 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
+ *gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
/* writable */ true, /* executable */ false,
/* includesGuardPages */ true);
- {
- ok = false;
- jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
- if (!ok)
- jitCallCountThreshold = 3;
- if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
- jitCallCountThreshold = std::numeric_limits<int>::max();
- }
-
exceptionValue = jsAlloca(1);
*exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
@@ -412,7 +443,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
- jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value);
+ jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
identifierTable = new IdentifierTable(this);
@@ -855,18 +886,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
- qMetaTypeId<QJSValue>();
- qMetaTypeId<QList<int> >();
-
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
- QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
- QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
- QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
- if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
- QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
-
QV4::QObjectWrapper::initializeBindings(this);
m_delayedCallQueue.init(this);
@@ -1978,12 +1997,12 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *dat
int ExecutionEngine::maxJSStackSize() const
{
- return m_maxJSStackSize;
+ return s_maxJSStackSize;
}
int ExecutionEngine::maxGCStackSize() const
{
- return m_maxGCStackSize;
+ return s_maxGCStackSize;
}
ReturnedValue ExecutionEngine::global()
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 617c7361cf..b9f2633d91 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -60,6 +60,7 @@
#include <private/qqmldelayedcallqueue_p.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qset.h>
#include "qv4function_p.h"
#include <private/qv4compileddata_p.h>
@@ -169,8 +170,6 @@ class ReactionHandler;
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
{
private:
- static qint32 maxCallDepth;
-
friend struct ExecutionContextSaver;
friend struct ExecutionContext;
friend struct Heap::ExecutionContext;
@@ -695,7 +694,7 @@ public:
return false;
if (f) {
return !f->aotFunction && !f->isGenerator()
- && f->interpreterCallCount >= jitCallCountThreshold;
+ && f->interpreterCallCount >= s_jitCallCountThreshold;
}
return true;
#else
@@ -772,13 +771,18 @@ public:
private:
QV4::ReturnedValue fromData(QMetaType type, const void *ptr, const QVariant *variant = nullptr);
+ static void initializeStaticMembers();
+
+ static int s_maxCallDepth;
+ static int s_jitCallCountThreshold;
+ static int s_maxJSStackSize;
+ static int s_maxGCStackSize;
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
#endif
QSet<QString> m_illegalNames;
- int jitCallCountThreshold;
// used by generated Promise objects to handle 'then' events
QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler;
@@ -797,9 +801,6 @@ private:
QHash<QString, quint32> m_consoleCount;
QVector<Deletable *> m_extensionData;
-
- int m_maxJSStackSize = 4 * 1024 * 1024;
- int m_maxGCStackSize = 2 * 1024 * 1024;
};
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
@@ -815,7 +816,7 @@ struct ExecutionEngineCallDepthRecorder
inline bool ExecutionEngine::checkStackLimits()
{
- if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
+ if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= s_maxCallDepth))) {
throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
return true;
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 1a5e76cbc9..6e0d07210c 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -69,6 +69,8 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/QScopedValueRollback>
+static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
+
#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
# ifdef Q_OS_LINUX
// Place on a separate section on Linux so it's easier to check from outside
@@ -856,7 +858,8 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
This function creates a temporary key vector and sorts it to guarantuee a stable
hash. This is used to calculate a check-sum on dependent meta-objects.
*/
-bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash) const
+bool ResolvedTypeReferenceMap::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
{
std::vector<int> keys (count());
int i = 0;
@@ -866,7 +869,7 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash) const
}
std::sort(keys.begin(), keys.end());
for (int key: keys) {
- if (!this->operator[](key)->addToHash(hash))
+ if (!this->operator[](key)->addToHash(hash, checksums))
return false;
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index 6798829898..c093033aab 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -94,7 +94,7 @@ class ResolvedTypeReference;
// map from name index
struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
{
- bool addToHash(QCryptographicHash *hash) const;
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
};
class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit,
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index ebce416172..d964fadd18 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -131,6 +131,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
QQmlPropertyData *propertyData;
} qobjectLookup;
struct {
+ quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc
+ quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
+ int coreIndex;
+ int notifyIndex;
+ } qobjectFallbackLookup;
+ struct {
Heap::InternalClass *ic;
quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index c9a9ab138a..f7a5f897c8 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -640,6 +640,11 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
+ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ return resolveQmlContextPropertyLookupGetter(l, engine, base);
+}
+
ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index cbbd04bff0..1da64b101f 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp
index 96bddccf83..4ac68611f4 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference.cpp
+++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp
@@ -100,11 +100,12 @@ QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache()
}
}
-bool ResolvedTypeReference::addToHash(QCryptographicHash *hash)
+bool ResolvedTypeReference::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums)
{
if (m_type.isValid() && !m_type.isInlineComponentType()) {
bool ok = false;
- hash->addData(createPropertyCache()->checksum(&ok));
+ hash->addData(createPropertyCache()->checksum(checksums, &ok));
return ok;
}
if (!m_compilationUnit)
diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h
index 61e683ce17..582c3a5d0a 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference_p.h
+++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h
@@ -75,7 +75,7 @@ public:
QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
QQmlRefPointer<QQmlPropertyCache> createPropertyCache();
- bool addToHash(QCryptographicHash *hash);
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums);
void doDynamicTypeCheck();
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 42ad6b8716..a265a67e24 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -129,8 +129,14 @@ public:
QVariant at(int index) const
{
const auto *p = d();
- QVariant result(valueMetaType(p));
- metaSequence(p)->valueAtIndex(p->container, index, result.data());
+ const QMetaType v = valueMetaType(p);
+ QVariant result;
+ if (v == QMetaType::fromType<QVariant>()) {
+ metaSequence(p)->valueAtIndex(p->container, index, &result);
+ } else {
+ result = QVariant(v);
+ metaSequence(p)->valueAtIndex(p->container, index, result.data());
+ }
return result;
}
@@ -141,6 +147,8 @@ public:
const QMetaType v = valueMetaType(p);
if (item.metaType() == v) {
m->addValueAtEnd(p->container, item.constData());
+ } else if (v == QMetaType::fromType<QVariant>()) {
+ m->addValueAtEnd(p->container, &item);
} else {
QVariant converted = item;
if (!converted.convert(v))
@@ -156,6 +164,8 @@ public:
const QMetaType v = valueMetaType(p);
if (item.metaType() == v) {
m->setValueAtIndex(p->container, index, item.constData());
+ } else if (v == QMetaType::fromType<QVariant>()) {
+ m->setValueAtIndex(p->container, index, &item);
} else {
QVariant converted = item;
if (!converted.convert(v))
@@ -270,8 +280,11 @@ public:
} else {
/* according to ECMA262r3 we need to insert */
/* the value at the given index, increasing length to index+1. */
- while (index > count++)
- append(QVariant(valueType));
+ while (index > count++) {
+ append(valueType == QMetaType::fromType<QVariant>()
+ ? QVariant()
+ : QVariant(valueType));
+ }
append(element);
}
@@ -687,15 +700,19 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
for (quint32 i = 0; i < length; ++i) {
const QMetaType valueMetaType = priv->typeId;
QVariant variant = scope.engine->toVariant(a->get(i), valueMetaType, false);
- const QMetaType originalType = variant.metaType();
- if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
- qWarning() << QLatin1String(
- "Could not convert array value at position %1 from %2 to %3")
- .arg(QString::number(i), QString::fromUtf8(originalType.name()),
- QString::fromUtf8(valueMetaType.name()));
- variant = QVariant(valueMetaType);
+ if (valueMetaType == QMetaType::fromType<QVariant>()) {
+ meta->addValueAtEnd(result.data(), &variant);
+ } else {
+ const QMetaType originalType = variant.metaType();
+ if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
+ qWarning() << QLatin1String(
+ "Could not convert array value at position %1 from %2 to %3")
+ .arg(QString::number(i), QString::fromUtf8(originalType.name()),
+ QString::fromUtf8(valueMetaType.name()));
+ variant = QVariant(valueMetaType);
+ }
+ meta->addValueAtEnd(result.data(), variant.constData());
}
- meta->addValueAtEnd(result.data(), variant.constData());
}
return result;
}
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 593a6696fb..ff09ec815c 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -61,8 +61,8 @@
#include "qv4string_p.h"
#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qitemselectionmodel.h>
#endif
QT_BEGIN_NAMESPACE
@@ -110,16 +110,13 @@ QT_DECLARE_SEQUENTIAL_CONTAINER(QRealStdVectorForeign, std::vector<qreal>, doubl
QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, double);
#endif
+QT_DECLARE_SEQUENTIAL_CONTAINER(QDoubleStdVectorForeign, std::vector<double>, double);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QFloatStdVectorForeign, std::vector<float>, float);
QT_DECLARE_SEQUENTIAL_CONTAINER(QIntStdVectorForeign, std::vector<int>, int);
QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolStdVectorForeign, std::vector<bool>, bool);
QT_DECLARE_SEQUENTIAL_CONTAINER(QStringStdVectorForeign, std::vector<QString>, QString);
QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlStdVectorForeign, std::vector<QUrl>, QUrl);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QIntListForeign, QList<int>, int);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolListForeign, QList<bool>, bool);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QStringListForeign, QStringList, QString);
-QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlListForeign, QList<QUrl>, QUrl);
-
#if QT_CONFIG(qml_itemmodel)
QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexListForeign, QModelIndexList, QModelIndex);
QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexStdVectorForeign, std::vector<QModelIndex>, QModelIndex);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 63e02ce631..c21f1b4e32 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -461,9 +461,17 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(argumentType.sizeOf() > 0);
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
- argumentType.construct(arg);
- if (frame->argc() > i)
- QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+
+ if (argumentType == QMetaType::fromType<QVariant>()) {
+ if (frame->argc() > i)
+ new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]);
+ else
+ new (arg) QVariant();
+ } else {
+ argumentType.construct(arg);
+ if (frame->argc() > i)
+ QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+ }
transformedArguments[i] = arg;
}
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 306f3d40f9..1ffdddc1e1 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1680,6 +1680,9 @@ Type: UiQualifiedId;
} break;
./
+Type: T_VAR;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
Type: T_VOID;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index 1b58f687e2..3ae5c405f2 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -80,31 +80,7 @@ static inline QChar convertHex(QChar c1, QChar c2)
return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
}
-Lexer::Lexer(Engine *engine)
- : _engine(engine)
- , _codePtr(nullptr)
- , _endPtr(nullptr)
- , _tokenStartPtr(nullptr)
- , _char(u'\n')
- , _errorCode(NoError)
- , _currentLineNumber(0)
- , _currentColumnNumber(0)
- , _tokenValue(0)
- , _parenthesesState(IgnoreParentheses)
- , _parenthesesCount(0)
- , _stackToken(-1)
- , _patternFlags(0)
- , _tokenKind(0)
- , _tokenLength(0)
- , _tokenLine(0)
- , _tokenColumn(0)
- , _validTokenText(false)
- , _prohibitAutomaticSemicolon(false)
- , _restrictedKeyword(false)
- , _terminator(false)
- , _followsClosingBrace(false)
- , _delimited(true)
- , _qmlMode(true)
+Lexer::Lexer(Engine *engine) : _engine(engine), _endPtr(nullptr), _qmlMode(true)
{
if (engine)
engine->setLexer(this);
@@ -127,66 +103,67 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_qmlMode = qmlMode;
_code = code;
- _tokenText.clear();
- _tokenText.reserve(1024);
- _errorMessage.clear();
- _tokenSpell = QStringView();
- _rawString = QStringView();
- _codePtr = code.unicode();
- _endPtr = _codePtr + code.length();
- _tokenStartPtr = _codePtr;
+ _state.tokenText.clear();
+ _state.tokenText.reserve(1024);
+ _state.errorMessage.clear();
+ _state.tokenSpell = QStringView();
+ _state.rawString = QStringView();
- _char = u'\n';
- _errorCode = NoError;
+ _state.codePtr = code.unicode();
+ _endPtr = _state.codePtr + code.length();
+ _state.tokenStartPtr = _state.codePtr;
- _currentLineNumber = lineno;
- _currentColumnNumber = 0;
- _tokenValue = 0;
+ _state.currentChar = u'\n';
+ _state.errorCode = NoError;
+
+ _state.currentLineNumber = lineno;
+ _state.currentColumnNumber = 0;
+ _state.tokenValue = 0;
// parentheses state
- _parenthesesState = IgnoreParentheses;
- _parenthesesCount = 0;
-
- _stackToken = -1;
-
- _patternFlags = 0;
- _tokenLength = 0;
- _tokenLine = lineno;
- _tokenColumn = 0;
-
- _validTokenText = false;
- _prohibitAutomaticSemicolon = false;
- _restrictedKeyword = false;
- _terminator = false;
- _followsClosingBrace = false;
- _delimited = true;
+ _state.parenthesesState = IgnoreParentheses;
+ _state.parenthesesCount = 0;
+
+ _state.stackToken = -1;
+
+ _state.patternFlags = 0;
+ _state.tokenLength = 0;
+ _state.tokenLine = lineno;
+ _state.tokenColumn = 0;
+
+ _state.validTokenText = false;
+ _state.prohibitAutomaticSemicolon = false;
+ _state.restrictedKeyword = false;
+ _state.terminator = false;
+ _state.followsClosingBrace = false;
+ _state.delimited = true;
}
void Lexer::scanChar()
{
- if (_skipLinefeed) {
- Q_ASSERT(*_codePtr == u'\n');
- ++_codePtr;
- _skipLinefeed = false;
+ if (_state.skipLinefeed) {
+ Q_ASSERT(*_state.codePtr == u'\n');
+ ++_state.codePtr;
+ _state.skipLinefeed = false;
}
- _char = *_codePtr++;
- ++_currentColumnNumber;
+ _state.currentChar = *_state.codePtr++;
+ ++_state.currentColumnNumber;
if (isLineTerminator()) {
- if (_char == u'\r') {
- if (_codePtr < _endPtr && *_codePtr == u'\n')
- _skipLinefeed = true;
- _char = u'\n';
+ if (_state.currentChar == u'\r') {
+ if (_state.codePtr < _endPtr && *_state.codePtr == u'\n')
+ _state.skipLinefeed = true;
+ _state.currentChar = u'\n';
}
- ++_currentLineNumber;
- _currentColumnNumber = 0;
+ ++_state.currentLineNumber;
+ _state.currentColumnNumber = 0;
}
}
QChar Lexer::peekChar()
{
- auto peekPtr = _codePtr;
+ auto peekPtr = _state.codePtr;
if (peekPtr < _endPtr)
return *peekPtr;
return QChar();
@@ -259,107 +236,107 @@ int octalDigit(QChar c)
int Lexer::lex()
{
- const int previousTokenKind = _tokenKind;
+ const int previousTokenKind = _state.tokenKind;
again:
- _tokenSpell = QStringView();
- _rawString = QStringView();
- _tokenKind = scanToken();
- _tokenLength = _codePtr - _tokenStartPtr - 1;
-
- _delimited = false;
- _restrictedKeyword = false;
- _followsClosingBrace = (previousTokenKind == T_RBRACE);
-
- // update the flags
- switch (_tokenKind) {
- case T_LBRACE:
- if (_bracesCount > 0)
- ++_bracesCount;
- Q_FALLTHROUGH();
- case T_SEMICOLON:
- _importState = ImportState::NoQmlImport;
- Q_FALLTHROUGH();
- case T_QUESTION:
- case T_COLON:
- case T_TILDE:
- _delimited = true;
- break;
- case T_AUTOMATIC_SEMICOLON:
- case T_AS:
- _importState = ImportState::NoQmlImport;
- Q_FALLTHROUGH();
- default:
- if (isBinop(_tokenKind))
- _delimited = true;
- break;
-
- case T_IMPORT:
- if (qmlMode() || (_handlingDirectives && previousTokenKind == T_DOT))
- _importState = ImportState::SawImport;
- if (isBinop(_tokenKind))
- _delimited = true;
- break;
-
- case T_IF:
- case T_FOR:
- case T_WHILE:
- case T_WITH:
- _parenthesesState = CountParentheses;
- _parenthesesCount = 0;
- break;
-
- case T_ELSE:
- case T_DO:
- _parenthesesState = BalancedParentheses;
- break;
-
- case T_CONTINUE:
- case T_BREAK:
- case T_RETURN:
- case T_YIELD:
- case T_THROW:
- _restrictedKeyword = true;
- break;
- case T_RBRACE:
- if (_bracesCount > 0)
- --_bracesCount;
- if (_bracesCount == 0)
- goto again;
- } // switch
+ _state.tokenSpell = QStringView();
+ _state.rawString = QStringView();
+ _state.tokenKind = scanToken();
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
+
+ _state.delimited = false;
+ _state.restrictedKeyword = false;
+ _state.followsClosingBrace = (previousTokenKind == T_RBRACE);
+
+ // update the flags
+ switch (_state.tokenKind) {
+ case T_LBRACE:
+ if (_state.bracesCount > 0)
+ ++_state.bracesCount;
+ Q_FALLTHROUGH();
+ case T_SEMICOLON:
+ _state.importState = ImportState::NoQmlImport;
+ Q_FALLTHROUGH();
+ case T_QUESTION:
+ case T_COLON:
+ case T_TILDE:
+ _state.delimited = true;
+ break;
+ case T_AUTOMATIC_SEMICOLON:
+ case T_AS:
+ _state.importState = ImportState::NoQmlImport;
+ Q_FALLTHROUGH();
+ default:
+ if (isBinop(_state.tokenKind))
+ _state.delimited = true;
+ break;
+
+ case T_IMPORT:
+ if (qmlMode() || (_state.handlingDirectives && previousTokenKind == T_DOT))
+ _state.importState = ImportState::SawImport;
+ if (isBinop(_state.tokenKind))
+ _state.delimited = true;
+ break;
+
+ case T_IF:
+ case T_FOR:
+ case T_WHILE:
+ case T_WITH:
+ _state.parenthesesState = CountParentheses;
+ _state.parenthesesCount = 0;
+ break;
+
+ case T_ELSE:
+ case T_DO:
+ _state.parenthesesState = BalancedParentheses;
+ break;
+
+ case T_CONTINUE:
+ case T_BREAK:
+ case T_RETURN:
+ case T_YIELD:
+ case T_THROW:
+ _state.restrictedKeyword = true;
+ break;
+ case T_RBRACE:
+ if (_state.bracesCount > 0)
+ --_state.bracesCount;
+ if (_state.bracesCount == 0)
+ goto again;
+ } // switch
// update the parentheses state
- switch (_parenthesesState) {
- case IgnoreParentheses:
- break;
-
- case CountParentheses:
- if (_tokenKind == T_RPAREN) {
- --_parenthesesCount;
- if (_parenthesesCount == 0)
- _parenthesesState = BalancedParentheses;
- } else if (_tokenKind == T_LPAREN) {
- ++_parenthesesCount;
- }
- break;
-
- case BalancedParentheses:
- if (_tokenKind != T_DO && _tokenKind != T_ELSE)
- _parenthesesState = IgnoreParentheses;
- break;
- } // switch
-
- return _tokenKind;
+ switch (_state.parenthesesState) {
+ case IgnoreParentheses:
+ break;
+
+ case CountParentheses:
+ if (_state.tokenKind == T_RPAREN) {
+ --_state.parenthesesCount;
+ if (_state.parenthesesCount == 0)
+ _state.parenthesesState = BalancedParentheses;
+ } else if (_state.tokenKind == T_LPAREN) {
+ ++_state.parenthesesCount;
+ }
+ break;
+
+ case BalancedParentheses:
+ if (_state.tokenKind != T_DO && _state.tokenKind != T_ELSE)
+ _state.parenthesesState = IgnoreParentheses;
+ break;
+ } // switch
+
+ return _state.tokenKind;
}
uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
{
- Q_ASSERT(_char == u'u');
+ Q_ASSERT(_state.currentChar == u'u');
scanChar(); // skip u
- if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) {
+ if (_state.codePtr + 4 <= _endPtr && isHexDigit(_state.currentChar)) {
uint codePoint = 0;
for (int i = 0; i < 4; ++i) {
- int digit = hexDigit(_char);
+ int digit = hexDigit(_state.currentChar);
if (digit < 0)
goto error;
codePoint *= 16;
@@ -369,15 +346,15 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
*ok = true;
return codePoint;
- } else if (_codePtr < _endPtr && _char == u'{') {
+ } else if (_state.codePtr < _endPtr && _state.currentChar == u'{') {
scanChar(); // skip '{'
uint codePoint = 0;
- if (!isHexDigit(_char))
+ if (!isHexDigit(_state.currentChar))
// need at least one hex digit
goto error;
- while (_codePtr <= _endPtr) {
- int digit = hexDigit(_char);
+ while (_state.codePtr <= _endPtr) {
+ int digit = hexDigit(_state.currentChar);
if (digit < 0)
break;
codePoint *= 16;
@@ -387,7 +364,7 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
scanChar();
}
- if (_char != u'}')
+ if (_state.currentChar != u'}')
goto error;
scanChar(); // skip '}'
@@ -398,8 +375,9 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
}
error:
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ _state.errorCode = IllegalUnicodeEscapeSequence;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
*ok = false;
return 0;
@@ -407,13 +385,13 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
QChar Lexer::decodeHexEscapeCharacter(bool *ok)
{
- if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) {
+ if (isHexDigit(_state.codePtr[0]) && isHexDigit(_state.codePtr[1])) {
scanChar();
- const QChar c1 = _char;
+ const QChar c1 = _state.currentChar;
scanChar();
- const QChar c2 = _char;
+ const QChar c2 = _state.currentChar;
scanChar();
if (ok)
@@ -481,36 +459,36 @@ static bool isIdentifierPart(uint ch)
int Lexer::scanToken()
{
- if (_stackToken != -1) {
- int tk = _stackToken;
- _stackToken = -1;
+ if (_state.stackToken != -1) {
+ int tk = _state.stackToken;
+ _state.stackToken = -1;
return tk;
}
- if (_bracesCount == 0) {
+ if (_state.bracesCount == 0) {
// we're inside a Template string
return scanString(TemplateContinuation);
}
-
- _terminator = false;
+ _state.terminator = false;
again:
- _validTokenText = false;
+ _state.validTokenText = false;
// handle comment can be called after a '/' has been read
// and returns true if it actually encountered a comment
- auto handleComment = [this](){
- if (_char == u'*') {
+ auto handleComment = [this]() {
+ if (_state.currentChar == u'*') {
scanChar();
- while (_codePtr <= _endPtr) {
- if (_char == u'*') {
+ while (_state.codePtr <= _endPtr) {
+ if (_state.currentChar == u'*') {
scanChar();
- if (_char == u'/') {
+ if (_state.currentChar == u'/') {
scanChar();
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
+ _engine->addComment(tokenOffset() + 2,
+ _state.codePtr - _state.tokenStartPtr - 1 - 4,
tokenStartLine(), tokenStartColumn() + 2);
}
@@ -520,30 +498,30 @@ again:
scanChar();
}
}
- } else if (_char == u'/') {
- while (_codePtr <= _endPtr && !isLineTerminator()) {
+ } else if (_state.currentChar == u'/') {
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
scanChar();
}
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
- tokenStartLine(), tokenStartColumn() + 2);
+ _engine->addComment(tokenOffset() + 2,
+ _state.codePtr - _state.tokenStartPtr - 1 - 2, tokenStartLine(),
+ tokenStartColumn() + 2);
}
return true;
}
return false;
};
-
- while (_char.isSpace()) {
+ while (_state.currentChar.isSpace()) {
if (isLineTerminator()) {
- if (_restrictedKeyword) {
+ if (_state.restrictedKeyword) {
// automatic semicolon insertion
- _tokenLine = _currentLineNumber;
- _tokenColumn = _currentColumnNumber;
- _tokenStartPtr = _codePtr - 1;
+ _state.tokenLine = _state.currentLineNumber;
+ _state.tokenColumn = _state.currentColumnNumber;
+ _state.tokenStartPtr = _state.codePtr - 1;
return T_SEMICOLON;
} else {
- _terminator = true;
+ _state.terminator = true;
syncProhibitAutomaticSemicolon();
}
}
@@ -551,14 +529,14 @@ again:
scanChar();
}
- _tokenStartPtr = _codePtr - 1;
- _tokenLine = _currentLineNumber;
- _tokenColumn = _currentColumnNumber;
+ _state.tokenStartPtr = _state.codePtr - 1;
+ _state.tokenLine = _state.currentLineNumber;
+ _state.tokenColumn = _state.currentColumnNumber;
- if (_codePtr > _endPtr)
+ if (_state.codePtr > _endPtr)
return EOF_SYMBOL;
- const QChar ch = _char;
+ const QChar ch = _state.currentChar;
scanChar();
switch (ch.unicode()) {
@@ -566,10 +544,10 @@ again:
case u'}': return T_RBRACE;
case u'|':
- if (_char == u'|') {
+ if (_state.currentChar == u'|') {
scanChar();
return T_OR_OR;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_OR_EQ;
}
@@ -578,7 +556,7 @@ again:
case u'{': return T_LBRACE;
case u'^':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_XOR_EQ;
}
@@ -587,11 +565,11 @@ again:
case u']': return T_RBRACKET;
case u'[': return T_LBRACKET;
case u'?': {
- if (_char == u'?') {
+ if (_state.currentChar == u'?') {
scanChar();
return T_QUESTION_QUESTION;
}
- if (_char == u'.' && !peekChar().isDigit()) {
+ if (_state.currentChar == u'.' && !peekChar().isDigit()) {
scanChar();
return T_QUESTION_DOT;
}
@@ -600,47 +578,47 @@ again:
}
case u'>':
- if (_char == u'>') {
+ if (_state.currentChar == u'>') {
scanChar();
- if (_char == u'>') {
+ if (_state.currentChar == u'>') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_GT_GT_GT_EQ;
}
return T_GT_GT_GT;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_GT_GT_EQ;
}
return T_GT_GT;
- } else if (_char == u'=') {
+ } else if (_state.currentChar == u'=') {
scanChar();
return T_GE;
}
return T_GT;
case u'=':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_EQ_EQ_EQ;
}
return T_EQ_EQ;
- } else if (_char == u'>') {
+ } else if (_state.currentChar == u'>') {
scanChar();
return T_ARROW;
}
return T_EQ;
case u'<':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_LE;
- } else if (_char == u'<') {
+ } else if (_state.currentChar == u'<') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_LT_LT_EQ;
}
@@ -654,39 +632,41 @@ again:
case u'/':
if (handleComment())
goto again;
- else if (_char == u'=') {
+ else if (_state.currentChar == u'=') {
scanChar();
return T_DIVIDE_EQ;
}
return T_DIVIDE_;
case u'.':
- if (_importState == ImportState::SawImport)
+ if (_state.importState == ImportState::SawImport)
return T_DOT;
- if (isDecimalDigit(_char.unicode()))
+ if (isDecimalDigit(_state.currentChar.unicode()))
return scanNumber(ch);
- if (_char == u'.') {
+ if (_state.currentChar == u'.') {
scanChar();
- if (_char == u'.') {
+ if (_state.currentChar == u'.') {
scanChar();
return T_ELLIPSIS;
} else {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
return T_ERROR;
}
}
return T_DOT;
case u'-':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_MINUS_EQ;
- } else if (_char == u'-') {
+ } else if (_state.currentChar == u'-') {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
- _stackToken = T_MINUS_MINUS;
+ if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon
+ && _state.tokenKind != T_LPAREN) {
+ _state.stackToken = T_MINUS_MINUS;
return T_SEMICOLON;
}
@@ -697,14 +677,15 @@ again:
case u',': return T_COMMA;
case u'+':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_PLUS_EQ;
- } else if (_char == u'+') {
+ } else if (_state.currentChar == u'+') {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
- _stackToken = T_PLUS_PLUS;
+ if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon
+ && _state.tokenKind != T_LPAREN) {
+ _state.stackToken = T_PLUS_PLUS;
return T_SEMICOLON;
}
@@ -713,12 +694,12 @@ again:
return T_PLUS;
case u'*':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_STAR_EQ;
- } else if (_char == u'*') {
+ } else if (_state.currentChar == u'*') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_STAR_STAR_EQ;
}
@@ -732,26 +713,26 @@ again:
case u'@': return T_AT;
case u'&':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_AND_EQ;
- } else if (_char == u'&') {
+ } else if (_state.currentChar == u'&') {
scanChar();
return T_AND_AND;
}
return T_AND;
case u'%':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_REMAINDER_EQ;
}
return T_REMAINDER;
case u'!':
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
- if (_char == u'=') {
+ if (_state.currentChar == u'=') {
scanChar();
return T_NOT_EQ_EQ;
}
@@ -760,7 +741,7 @@ again:
return T_NOT;
case u'`':
- _outerTemplateBraceCount.push(_bracesCount);
+ _state.outerTemplateBraceCount.push(_state.bracesCount);
Q_FALLTHROUGH();
case u'\'':
case u'"':
@@ -775,19 +756,19 @@ again:
case u'7':
case u'8':
case u'9':
- if (_importState == ImportState::SawImport)
+ if (_state.importState == ImportState::SawImport)
return scanVersionNumber(ch);
else
return scanNumber(ch);
case '#':
- if (_currentLineNumber == 1 && _currentColumnNumber == 2) {
+ if (_state.currentLineNumber == 1 && _state.currentColumnNumber == 2) {
// shebang support
- while (_codePtr <= _endPtr && !isLineTerminator()) {
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
scanChar();
}
if (_engine) {
- _engine->addComment(tokenOffset(), _codePtr - _tokenStartPtr - 1,
+ _engine->addComment(tokenOffset(), _state.codePtr - _state.tokenStartPtr - 1,
tokenStartLine(), tokenStartColumn());
}
goto again;
@@ -797,10 +778,10 @@ again:
default: {
uint c = ch.unicode();
bool identifierWithEscapeChars = false;
- if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) {
- c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_state.currentChar.unicode())) {
+ c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode());
scanChar();
- } else if (c == '\\' && _char == u'u') {
+ } else if (c == '\\' && _state.currentChar == u'u') {
identifierWithEscapeChars = true;
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
@@ -809,26 +790,27 @@ again:
}
if (isIdentifierStart(c)) {
if (identifierWithEscapeChars) {
- _tokenText.resize(0);
+ _state.tokenText.resize(0);
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
- _validTokenText = true;
+ _state.validTokenText = true;
}
- while (_codePtr <= _endPtr) {
- c = _char.unicode();
- if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) {
+ while (_state.codePtr <= _endPtr) {
+ c = _state.currentChar.unicode();
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_state.codePtr->unicode())) {
scanChar();
- c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
- } else if (_char == u'\\' && _codePtr[0] == u'u') {
+ c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode());
+ } else if (_state.currentChar == u'\\' && _state.codePtr[0] == u'u') {
if (!identifierWithEscapeChars) {
identifierWithEscapeChars = true;
- _tokenText.resize(0);
- _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
- _validTokenText = true;
+ _state.tokenText.resize(0);
+ _state.tokenText.insert(0, _state.tokenStartPtr,
+ _state.codePtr - _state.tokenStartPtr - 1);
+ _state.validTokenText = true;
}
scanChar(); // skip '\\'
@@ -841,10 +823,10 @@ again:
break;
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
continue;
}
@@ -854,42 +836,43 @@ again:
if (identifierWithEscapeChars) {
if (QChar::requiresSurrogates(c)) {
- _tokenText += QChar(QChar::highSurrogate(c));
- _tokenText += QChar(QChar::lowSurrogate(c));
+ _state.tokenText += QChar(QChar::highSurrogate(c));
+ _state.tokenText += QChar(QChar::lowSurrogate(c));
} else {
- _tokenText += QChar(c);
+ _state.tokenText += QChar(c);
}
}
scanChar();
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
int kind = T_IDENTIFIER;
if (!identifierWithEscapeChars)
- kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags());
+ kind = classify(_state.tokenStartPtr, _state.tokenLength, parseModeFlags());
if (kind == T_FUNCTION) {
continue_skipping:
- while (_codePtr < _endPtr && _char.isSpace())
- scanChar();
- if (_char == u'*') {
- _tokenLength = _codePtr - _tokenStartPtr - 1;
- kind = T_FUNCTION_STAR;
- scanChar();
- } else if (_char == u'/') {
- scanChar();
- if (handleComment())
- goto continue_skipping;
- }
+ while (_state.codePtr < _endPtr && _state.currentChar.isSpace())
+ scanChar();
+ if (_state.currentChar == u'*') {
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
+ kind = T_FUNCTION_STAR;
+ scanChar();
+ } else if (_state.currentChar == u'/') {
+ scanChar();
+ if (handleComment())
+ goto continue_skipping;
+ }
}
if (_engine) {
if (kind == T_IDENTIFIER && identifierWithEscapeChars)
- _tokenSpell = _engine->newStringRef(_tokenText);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
else
- _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
+ _state.tokenSpell = _engine->midRef(_state.tokenStartPtr - _code.unicode(),
+ _state.tokenLength);
}
return kind;
@@ -907,34 +890,36 @@ int Lexer::scanString(ScanStringMode mode)
QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
bool multilineStringLiteral = false;
- const QChar *startCode = _codePtr - 1;
+ const QChar *startCode = _state.codePtr - 1;
// in case we just parsed a \r, we need to reset this flag to get things working
// correctly in the loop below and afterwards
- _skipLinefeed = false;
+ _state.skipLinefeed = false;
bool first = true;
if (_engine) {
- while (_codePtr <= _endPtr) {
+ while (_state.codePtr <= _endPtr) {
if (isLineTerminator()) {
if ((quote == u'`' || qmlMode())) {
if (first)
- --_currentLineNumber; // will be read again in scanChar()
+ --_state.currentLineNumber; // will be read again in scanChar()
break;
}
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Stray newline in string literal");
return T_ERROR;
- } else if (_char == u'\\') {
+ } else if (_state.currentChar == u'\\') {
break;
- } else if (_char == u'$' && quote == u'`') {
+ } else if (_state.currentChar == u'$' && quote == u'`') {
break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1);
- _rawString = _tokenSpell;
+ } else if (_state.currentChar == quote) {
+ _state.tokenSpell = _engine->midRef(startCode - _code.unicode(),
+ _state.codePtr - startCode - 1);
+ _state.rawString = _state.tokenSpell;
scanChar();
if (quote == u'`')
- _bracesCount = _outerTemplateBraceCount.pop();
+ _state.bracesCount = _state.outerTemplateBraceCount.pop();
if (mode == TemplateHead)
return T_NO_SUBSTITUTION_TEMPLATE;
@@ -944,40 +929,40 @@ int Lexer::scanString(ScanStringMode mode)
return T_STRING_LITERAL;
}
// don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result
- _char = *_codePtr++;
- ++_currentColumnNumber;
+ _state.currentChar = *_state.codePtr++;
+ ++_state.currentColumnNumber;
first = false;
}
}
// rewind by one char, so things gets scanned correctly
- --_codePtr;
- --_currentColumnNumber;
+ --_state.codePtr;
+ --_state.currentColumnNumber;
- _validTokenText = true;
- _tokenText = QString(startCode, _codePtr - startCode);
+ _state.validTokenText = true;
+ _state.tokenText = QString(startCode, _state.codePtr - startCode);
auto setRawString = [&](const QChar *end) {
QString raw(startCode, end - startCode - 1);
raw.replace(QLatin1String("\r\n"), QLatin1String("\n"));
raw.replace(u'\r', u'\n');
- _rawString = _engine->newStringRef(raw);
+ _state.rawString = _engine->newStringRef(raw);
};
scanChar();
- while (_codePtr <= _endPtr) {
- if (_char == quote) {
+ while (_state.codePtr <= _endPtr) {
+ if (_state.currentChar == quote) {
scanChar();
if (_engine) {
- _tokenSpell = _engine->newStringRef(_tokenText);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
if (quote == u'`')
- setRawString(_codePtr - 1);
+ setRawString(_state.codePtr - 1);
}
if (quote == u'`')
- _bracesCount = _outerTemplateBraceCount.pop();
+ _state.bracesCount = _state.outerTemplateBraceCount.pop();
if (mode == TemplateContinuation)
return T_TEMPLATE_TAIL;
@@ -985,27 +970,28 @@ int Lexer::scanString(ScanStringMode mode)
return T_NO_SUBSTITUTION_TEMPLATE;
return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
- } else if (quote == u'`' && _char == u'$' && *_codePtr == u'{') {
+ } else if (quote == u'`' && _state.currentChar == u'$' && *_state.codePtr == u'{') {
scanChar();
scanChar();
- _bracesCount = 1;
+ _state.bracesCount = 1;
if (_engine) {
- _tokenSpell = _engine->newStringRef(_tokenText);
- setRawString(_codePtr - 2);
+ _state.tokenSpell = _engine->newStringRef(_state.tokenText);
+ setRawString(_state.codePtr - 2);
}
return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
- } else if (_char == u'\\') {
+ } else if (_state.currentChar == u'\\') {
scanChar();
- if (_codePtr > _endPtr) {
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ if (_state.codePtr > _endPtr) {
+ _state.errorCode = IllegalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "End of file reached at escape sequence");
return T_ERROR;
}
QChar u;
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
// unicode escape sequence
case u'u': {
bool ok = false;
@@ -1014,7 +1000,7 @@ int Lexer::scanString(ScanStringMode mode)
return T_ERROR;
if (QChar::requiresSurrogates(codePoint)) {
// need to use a surrogate pair
- _tokenText += QChar(QChar::highSurrogate(codePoint));
+ _state.tokenText += QChar(QChar::highSurrogate(codePoint));
u = QChar::lowSurrogate(codePoint);
} else {
u = QChar(codePoint);
@@ -1026,8 +1012,9 @@ int Lexer::scanString(ScanStringMode mode)
bool ok = false;
u = decodeHexEscapeCharacter(&ok);
if (!ok) {
- _errorCode = IllegalHexadecimalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
+ _state.errorCode = IllegalHexadecimalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Illegal hexadecimal escape sequence");
return T_ERROR;
}
} break;
@@ -1044,7 +1031,7 @@ int Lexer::scanString(ScanStringMode mode)
case u'v': u = u'\v'; scanChar(); break;
case u'0':
- if (! _codePtr->isDigit()) {
+ if (!_state.codePtr->isDigit()) {
scanChar();
u = u'\0';
break;
@@ -1059,8 +1046,9 @@ int Lexer::scanString(ScanStringMode mode)
case u'7':
case u'8':
case u'9':
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
+ _state.errorCode = IllegalEscapeSequence;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Octal escape sequences are not allowed");
return T_ERROR;
case u'\r':
@@ -1072,40 +1060,45 @@ int Lexer::scanString(ScanStringMode mode)
default:
// non escape character
- u = _char;
+ u = _state.currentChar;
scanChar();
}
- _tokenText += u;
+ _state.tokenText += u;
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
}
- _errorCode = UnclosedStringLiteral;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
+ _state.errorCode = UnclosedStringLiteral;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
return T_ERROR;
}
int Lexer::scanNumber(QChar ch)
{
if (ch == u'0') {
- if (_char == u'x' || _char == u'X') {
- ch = _char; // remember the x or X to use it in the error message below.
+ if (_state.currentChar == u'x' || _state.currentChar == u'X') {
+ ch = _state.currentChar; // remember the x or X to use it in the error message below.
// parse hex integer literal
scanChar(); // consume 'x'
- if (!isHexDigit(_char)) {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ if (!isHexDigit(_state.currentChar)) {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser",
+ "At least one hexadecimal digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
- int digit = ::hexDigit(_char);
+ int digit = ::hexDigit(_state.currentChar);
if (digit < 0)
break;
d *= 16;
@@ -1113,23 +1106,26 @@ int Lexer::scanNumber(QChar ch)
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char == u'o' || _char == u'O') {
- ch = _char; // remember the o or O to use it in the error message below.
+ } else if (_state.currentChar == u'o' || _state.currentChar == u'O') {
+ ch = _state.currentChar; // remember the o or O to use it in the error message below.
// parse octal integer literal
scanChar(); // consume 'o'
- if (!isOctalDigit(_char.unicode())) {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch);
+ if (!isOctalDigit(_state.currentChar.unicode())) {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser", "At least one octal digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
- int digit = ::octalDigit(_char);
+ int digit = ::octalDigit(_state.currentChar);
if (digit < 0)
break;
d *= 8;
@@ -1137,37 +1133,41 @@ int Lexer::scanNumber(QChar ch)
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char == u'b' || _char == u'B') {
- ch = _char; // remember the b or B to use it in the error message below.
+ } else if (_state.currentChar == u'b' || _state.currentChar == u'B') {
+ ch = _state.currentChar; // remember the b or B to use it in the error message below.
// parse binary integer literal
scanChar(); // consume 'b'
- if (_char.unicode() != u'0' && _char.unicode() != u'1') {
- _errorCode = IllegalNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch);
+ if (_state.currentChar.unicode() != u'0' && _state.currentChar.unicode() != u'1') {
+ _state.errorCode = IllegalNumber;
+ _state.errorMessage =
+ QCoreApplication::translate(
+ "QQmlParser", "At least one binary digit is required after '0%1'")
+ .arg(ch);
return T_ERROR;
}
double d = 0.;
while (1) {
int digit = 0;
- if (_char.unicode() == u'1')
+ if (_state.currentChar.unicode() == u'1')
digit = 1;
- else if (_char.unicode() != u'0')
+ else if (_state.currentChar.unicode() != u'0')
break;
d *= 2;
d += digit;
scanChar();
}
- _tokenValue = d;
+ _state.tokenValue = d;
return T_NUMERIC_LITERAL;
- } else if (_char.isDigit() && !qmlMode()) {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
+ } else if (_state.currentChar.isDigit() && !qmlMode()) {
+ _state.errorCode = IllegalCharacter;
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Decimal numbers can't start with '0'");
return T_ERROR;
}
}
@@ -1177,36 +1177,37 @@ int Lexer::scanNumber(QChar ch)
chars.append(ch.unicode());
if (ch != u'.') {
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume the digit
}
- if (_char == u'.') {
- chars.append(_char.unicode());
+ if (_state.currentChar == u'.') {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume `.'
}
}
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar();
}
- if (_char == u'e' || _char == u'E') {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == u'+' || _codePtr[0] == u'-') &&
- _codePtr[1].isDigit())) {
+ if (_state.currentChar == u'e' || _state.currentChar == u'E') {
+ if (_state.codePtr[0].isDigit()
+ || ((_state.codePtr[0] == u'+' || _state.codePtr[0] == u'-')
+ && _state.codePtr[1].isDigit())) {
- chars.append(_char.unicode());
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume `e'
- if (_char == u'+' || _char == u'-') {
- chars.append(_char.unicode());
+ if (_state.currentChar == u'+' || _state.currentChar == u'-') {
+ chars.append(_state.currentChar.unicode());
scanChar(); // consume the sign
}
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ while (_state.currentChar.isDigit()) {
+ chars.append(_state.currentChar.unicode());
scanChar();
}
}
@@ -1216,11 +1217,12 @@ int Lexer::scanNumber(QChar ch)
const char *end = nullptr;
bool ok = false;
- _tokenValue = qstrntod(begin, chars.size(), &end, &ok);
+ _state.tokenValue = qstrntod(begin, chars.size(), &end, &ok);
if (end - begin != chars.size()) {
- _errorCode = IllegalExponentIndicator;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
+ _state.errorCode = IllegalExponentIndicator;
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
return T_ERROR;
}
@@ -1230,108 +1232,114 @@ int Lexer::scanNumber(QChar ch)
int Lexer::scanVersionNumber(QChar ch)
{
if (ch == u'0') {
- _tokenValue = 0;
+ _state.tokenValue = 0;
return T_VERSION_NUMBER;
}
int acc = 0;
acc += ch.digitValue();
- while (_char.isDigit()) {
+ while (_state.currentChar.isDigit()) {
acc *= 10;
- acc += _char.digitValue();
+ acc += _state.currentChar.digitValue();
scanChar(); // consume the digit
}
- _tokenValue = acc;
+ _state.tokenValue = acc;
return T_VERSION_NUMBER;
}
bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
{
- _tokenText.resize(0);
- _validTokenText = true;
- _patternFlags = 0;
+ _state.tokenText.resize(0);
+ _state.validTokenText = true;
+ _state.patternFlags = 0;
if (prefix == EqualPrefix)
- _tokenText += u'=';
+ _state.tokenText += u'=';
while (true) {
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
case u'/':
scanChar();
// scan the flags
- _patternFlags = 0;
- while (isIdentLetter(_char)) {
- int flag = regExpFlagFromChar(_char);
- if (flag == 0 || _patternFlags & flag) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Invalid regular expression flag '%0'")
- .arg(QChar(_char));
+ _state.patternFlags = 0;
+ while (isIdentLetter(_state.currentChar)) {
+ int flag = regExpFlagFromChar(_state.currentChar);
+ if (flag == 0 || _state.patternFlags & flag) {
+ _state.errorMessage =
+ QCoreApplication::translate("QQmlParser",
+ "Invalid regular expression flag '%0'")
+ .arg(QChar(_state.currentChar));
return false;
}
- _patternFlags |= flag;
+ _state.patternFlags |= flag;
scanChar();
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _state.tokenLength = _state.codePtr - _state.tokenStartPtr - 1;
return true;
case u'\\':
// regular expression backslash sequence
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression backslash sequence");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
break;
case u'[':
// regular expression class
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- while (_codePtr <= _endPtr && ! isLineTerminator()) {
- if (_char == u']')
+ while (_state.codePtr <= _endPtr && !isLineTerminator()) {
+ if (_state.currentChar == u']')
break;
- else if (_char == u'\\') {
+ else if (_state.currentChar == u'\\') {
// regular expression backslash sequence
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression backslash sequence");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
}
- if (_char != u']') {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression class");
+ if (_state.currentChar != u']') {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression class");
return false;
}
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar(); // skip ]
break;
default:
- if (_codePtr > _endPtr || isLineTerminator()) {
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression literal");
+ if (_state.codePtr > _endPtr || isLineTerminator()) {
+ _state.errorMessage = QCoreApplication::translate(
+ "QQmlParser", "Unterminated regular expression literal");
return false;
} else {
- _tokenText += _char;
+ _state.tokenText += _state.currentChar;
scanChar();
}
} // switch
@@ -1342,7 +1350,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
bool Lexer::isLineTerminator() const
{
- const ushort unicode = _char.unicode();
+ const ushort unicode = _state.currentChar.unicode();
return unicode == 0x000Au
|| unicode == 0x000Du
|| unicode == 0x2028u
@@ -1351,13 +1359,13 @@ bool Lexer::isLineTerminator() const
unsigned Lexer::isLineTerminatorSequence() const
{
- switch (_char.unicode()) {
+ switch (_state.currentChar.unicode()) {
case 0x000Au:
case 0x2028u:
case 0x2029u:
return 1;
case 0x000Du:
- if (_codePtr->unicode() == 0x000Au)
+ if (_state.codePtr->unicode() == 0x000Au)
return 2;
else
return 1;
@@ -1398,54 +1406,52 @@ bool Lexer::isOctalDigit(ushort c)
QString Lexer::tokenText() const
{
- if (_validTokenText)
- return _tokenText;
+ if (_state.validTokenText)
+ return _state.tokenText;
- if (_tokenKind == T_STRING_LITERAL)
- return QString(_tokenStartPtr + 1, _tokenLength - 2);
+ if (_state.tokenKind == T_STRING_LITERAL)
+ return QString(_state.tokenStartPtr + 1, _state.tokenLength - 2);
- return QString(_tokenStartPtr, _tokenLength);
+ return QString(_state.tokenStartPtr, _state.tokenLength);
}
Lexer::Error Lexer::errorCode() const
{
- return _errorCode;
+ return _state.errorCode;
}
QString Lexer::errorMessage() const
{
- return _errorMessage;
+ return _state.errorMessage;
}
void Lexer::syncProhibitAutomaticSemicolon()
{
- if (_parenthesesState == BalancedParentheses) {
+ if (_state.parenthesesState == BalancedParentheses) {
// we have seen something like "if (foo)", which means we should
// never insert an automatic semicolon at this point, since it would
// then be expanded into an empty statement (ECMA-262 7.9.1)
- _prohibitAutomaticSemicolon = true;
- _parenthesesState = IgnoreParentheses;
+ _state.prohibitAutomaticSemicolon = true;
+ _state.parenthesesState = IgnoreParentheses;
} else {
- _prohibitAutomaticSemicolon = false;
+ _state.prohibitAutomaticSemicolon = false;
}
}
bool Lexer::prevTerminator() const
{
- return _terminator;
+ return _state.terminator;
}
bool Lexer::followsClosingBrace() const
{
- return _followsClosingBrace;
+ return _state.followsClosingBrace;
}
bool Lexer::canInsertAutomaticSemicolon(int token) const
{
- return token == T_RBRACE
- || token == EOF_SYMBOL
- || _terminator
- || _followsClosingBrace;
+ return token == T_RBRACE || token == EOF_SYMBOL || _state.terminator
+ || _state.followsClosingBrace;
}
static const int uriTokens[] = {
@@ -1509,12 +1515,12 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
error->loc.startColumn = tokenStartColumn();
};
- QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true);
+ QScopedValueRollback<bool> directivesGuard(_state.handlingDirectives, true);
Q_ASSERT(!_qmlMode);
lex(); // fetch the first token
- if (_tokenKind != T_DOT)
+ if (_state.tokenKind != T_DOT)
return true;
do {
@@ -1523,7 +1529,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
lex(); // skip T_DOT
- if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_IMPORT))
+ if (!(_state.tokenKind == T_IDENTIFIER || _state.tokenKind == T_IMPORT))
return true; // expected a valid QML/JS directive
const QString directiveName = tokenText();
@@ -1553,7 +1559,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
QString version;
bool fileImport = false; // file or uri import
- if (_tokenKind == T_STRING_LITERAL) {
+ if (_state.tokenKind == T_STRING_LITERAL) {
// .import T_STRING_LITERAL as T_IDENTIFIER
fileImport = true;
@@ -1565,10 +1571,10 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
}
lex();
- } else if (_tokenKind == T_IDENTIFIER) {
+ } else if (_state.tokenKind == T_IDENTIFIER) {
// .import T_IDENTIFIER (. T_IDENTIFIER)* (T_VERSION_NUMBER (. T_VERSION_NUMBER)?)? as T_IDENTIFIER
while (true) {
- if (!isUriToken(_tokenKind)) {
+ if (!isUriToken(_state.tokenKind)) {
setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
@@ -1580,7 +1586,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
- if (_tokenKind != QQmlJSGrammar::T_DOT)
+ if (_state.tokenKind != QQmlJSGrammar::T_DOT)
break;
pathOrUri.append(u'.');
@@ -1592,13 +1598,13 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
}
}
- if (_tokenKind == T_VERSION_NUMBER) {
+ if (_state.tokenKind == T_VERSION_NUMBER) {
version = tokenText();
lex();
- if (_tokenKind == T_DOT) {
+ if (_state.tokenKind == T_DOT) {
version += u'.';
lex();
- if (_tokenKind != T_VERSION_NUMBER) {
+ if (_state.tokenKind != T_VERSION_NUMBER) {
setError(QCoreApplication::translate(
"QQmlParser", "Incomplete version number (dot but no minor)"));
return false; // expected the module version number
@@ -1612,7 +1618,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
//
// recognize the mandatory `as' followed by the module name
//
- if (! (_tokenKind == T_AS && tokenStartLine() == lineNumber)) {
+ if (!(_state.tokenKind == T_AS && tokenStartLine() == lineNumber)) {
if (fileImport)
setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier"));
else
@@ -1651,7 +1657,16 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
// fetch the first token after the .pragma/.import directive
lex();
- } while (_tokenKind == T_DOT);
+ } while (_state.tokenKind == T_DOT);
return true;
}
+
+const Lexer::State &Lexer::state() const
+{
+ return _state;
+}
+void Lexer::setState(const Lexer::State &state)
+{
+ _state = state;
+}
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index b7ac4a3940..8c2f526de5 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -144,7 +144,7 @@ public:
}
bool qmlMode() const;
- bool yieldIsKeyWord() const { return _generatorLevel != 0; }
+ bool yieldIsKeyWord() const { return _state.generatorLevel != 0; }
void setStaticIsKeyword(bool b) { _staticIsKeyword = b; }
QString code() const;
@@ -155,19 +155,19 @@ public:
bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
bool scanDirectives(Directives *directives, DiagnosticMessage *error);
- int regExpFlags() const { return _patternFlags; }
- QString regExpPattern() const { return _tokenText; }
+ int regExpFlags() const { return _state.patternFlags; }
+ QString regExpPattern() const { return _state.tokenText; }
- int tokenKind() const { return _tokenKind; }
- int tokenOffset() const { return _tokenStartPtr - _code.unicode(); }
- int tokenLength() const { return _tokenLength; }
+ int tokenKind() const { return _state.tokenKind; }
+ int tokenOffset() const { return _state.tokenStartPtr - _code.unicode(); }
+ int tokenLength() const { return _state.tokenLength; }
- int tokenStartLine() const { return _tokenLine; }
- int tokenStartColumn() const { return _tokenColumn; }
+ int tokenStartLine() const { return _state.tokenLine; }
+ int tokenStartColumn() const { return _state.tokenColumn; }
- inline QStringView tokenSpell() const { return _tokenSpell; }
- inline QStringView rawString() const { return _rawString; }
- double tokenValue() const { return _tokenValue; }
+ inline QStringView tokenSpell() const { return _state.tokenSpell; }
+ inline QStringView rawString() const { return _state.rawString; }
+ double tokenValue() const { return _state.tokenValue; }
QString tokenText() const;
Error errorCode() const;
@@ -183,8 +183,56 @@ public:
BalancedParentheses
};
- void enterGeneratorBody() { ++_generatorLevel; }
- void leaveGeneratorBody() { --_generatorLevel; }
+ void enterGeneratorBody() { ++_state.generatorLevel; }
+ void leaveGeneratorBody() { --_state.generatorLevel; }
+
+ struct State
+ {
+ QString tokenText;
+ QString errorMessage;
+ QStringView tokenSpell;
+ QStringView rawString;
+
+ const QChar *codePtr = nullptr;
+ const QChar *tokenStartPtr = nullptr;
+
+ QChar currentChar = u'\n';
+ Error errorCode = NoError;
+
+ int currentLineNumber = 0;
+ int currentColumnNumber = 0;
+ double tokenValue = 0;
+
+ // parentheses state
+ ParenthesesState parenthesesState = IgnoreParentheses;
+ int parenthesesCount = 0;
+
+ // template string stack
+ QStack<int> outerTemplateBraceCount;
+ int bracesCount = -1;
+
+ int stackToken = -1;
+
+ int patternFlags = 0;
+ int tokenKind = 0;
+ int tokenLength = 0;
+ int tokenLine = 0;
+ int tokenColumn = 0;
+ ImportState importState = ImportState::NoQmlImport;
+
+ bool validTokenText = false;
+ bool prohibitAutomaticSemicolon = false;
+ bool restrictedKeyword = false;
+ bool terminator = false;
+ bool followsClosingBrace = false;
+ bool delimited = true;
+ bool skipLinefeed = false;
+ bool handlingDirectives = false;
+ int generatorLevel = 0;
+ };
+
+ const State &state() const;
+ void setState(const State &state);
protected:
static int classify(const QChar *s, int n, int parseModeFlags);
@@ -218,50 +266,11 @@ private:
Engine *_engine;
QString _code;
- QString _tokenText;
- QString _errorMessage;
- QStringView _tokenSpell;
- QStringView _rawString;
-
- const QChar *_codePtr;
const QChar *_endPtr;
- const QChar *_tokenStartPtr;
-
- QChar _char;
- Error _errorCode;
-
- int _currentLineNumber;
- int _currentColumnNumber;
- double _tokenValue;
-
- // parentheses state
- ParenthesesState _parenthesesState;
- int _parenthesesCount;
-
- // template string stack
- QStack<int> _outerTemplateBraceCount;
- int _bracesCount = -1;
-
- int _stackToken;
-
- int _patternFlags;
- int _tokenKind;
- int _tokenLength;
- int _tokenLine;
- int _tokenColumn;
- ImportState _importState = ImportState::NoQmlImport;
-
- bool _validTokenText;
- bool _prohibitAutomaticSemicolon;
- bool _restrictedKeyword;
- bool _terminator;
- bool _followsClosingBrace;
- bool _delimited;
bool _qmlMode;
- bool _skipLinefeed = false;
- int _generatorLevel = 0;
bool _staticIsKeyword = false;
- bool _handlingDirectives = false;
+
+ State _state;
};
} // end of namespace QQmlJS
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index cfcc8d5a2b..43cb973691 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -379,9 +379,9 @@ static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
return revisions;
const int propertyOffset = metaObject->propertyOffset();
const int propertyCount = metaObject->propertyCount();
- for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
- propertyIndex < propertyEnd; ++propertyIndex) {
- const QMetaProperty property = metaObject->property(propertyIndex);
+ for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
+ coreIndex < propertyEnd; ++coreIndex) {
+ const QMetaProperty property = metaObject->property(coreIndex);
if (int revision = property.revision())
revisions.append(QTypeRevision::fromEncodedVersion(revision));
}
@@ -528,21 +528,23 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
uniqueRevisions(&revisions, type.version, added);
for (QTypeRevision revision : revisions) {
- if (revision < added)
- continue;
if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
break;
- // When removed, we still add revisions, but anonymous ones
- if (removed.isValid() && !(revision < removed)) {
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
typeRevision.elementName = nullptr;
typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
} else {
typeRevision.elementName = elementName;
typeRevision.create = creatable ? type.create : nullptr;
typeRevision.userdata = type.userdata;
}
- assignVersions(&typeRevision, revision, type.version);
typeRevision.customParser = type.customParserFactory();
const int id = qmlregister(TypeRegistration, &typeRevision);
if (type.qmlTypeIds)
@@ -589,13 +591,14 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
uniqueRevisions(&revisions, type.version, added);
for (QTypeRevision revision : qAsConst(revisions)) {
- if (revision < added)
- continue;
if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
break;
- // When removed, we still add revisions, but anonymous ones
- if (removed.isValid() && !(revision < removed)) {
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
revisionRegistration.typeName = nullptr;
revisionRegistration.qObjectApi = nullptr;
} else {
@@ -603,7 +606,6 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
revisionRegistration.qObjectApi = type.qObjectApi;
}
- assignVersions(&revisionRegistration, revision, type.version);
const int id = qmlregister(SingletonRegistration, &revisionRegistration);
if (type.qmlTypeIds)
type.qmlTypeIds->append(id);
@@ -773,6 +775,21 @@ void AOTCompiledContext::setReturnValueUndefined() const
}
}
+static void captureFallbackProperty(
+ QObject *object, int coreIndex, int notifyIndex, bool isConstant,
+ QQmlContextData *qmlContext)
+{
+ if (!qmlContext || isConstant)
+ return;
+
+ QQmlEngine *engine = qmlContext->engine();
+ Q_ASSERT(engine);
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ Q_ASSERT(ep);
+ if (QQmlPropertyCapture *capture = ep->propertyCapture)
+ capture->captureProperty(object, coreIndex, notifyIndex);
+}
+
static void captureObjectProperty(
QObject *object, const QQmlPropertyCache *propertyCache,
const QQmlPropertyData *property, QQmlContextData *qmlContext)
@@ -799,8 +816,8 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
-static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object, void *target,
- QQmlContextData *qmlContext)
+static ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{
QQmlData *qmlData = QQmlData::get(object);
if (!qmlData)
@@ -822,6 +839,33 @@ static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object,
return ObjectPropertyResult::OK;
}
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+{
+ QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
+ qmlData->flushPendingBinding(coreIndex);
+
+ captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+
+ void *a[] = { target, nullptr };
+ metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{
const QQmlData *qmlData = QQmlData::get(object);
@@ -838,7 +882,62 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object,
return ObjectPropertyResult::OK;
}
-static bool initObjectLookup(
+static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ const QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
+
+ void *args[] = { value, nullptr };
+ metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
+ return ObjectPropertyResult::OK;
+}
+
+static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
+{
+ if (!lookupType.isValid()) {
+ // If type is invalid, then the calling code depends on the lookup
+ // to be set up in order to query the type, via lookupResultMetaType.
+ // We cannot verify the type in this case.
+ } else if ((lookupType.flags() & QMetaType::IsQmlList)
+ && (propertyType.flags() & QMetaType::IsQmlList)) {
+ // We want to check the value types here, but we cannot easily do it.
+ // Internally those are all QObject* lists, though.
+ } else if (lookupType.flags() & QMetaType::PointerToQObject) {
+ // We accept any base class as type, too
+
+ const QMetaObject *typeMetaObject = lookupType.metaObject();
+ const QMetaObject *foundMetaObject = propertyType.metaObject();
+ if (!foundMetaObject)
+ foundMetaObject = QQmlMetaType::metaObjectForType(propertyType).metaObject();
+
+ while (foundMetaObject && foundMetaObject != typeMetaObject)
+ foundMetaObject = foundMetaObject->superClass();
+
+ if (!foundMetaObject)
+ return false;
+ } else if (propertyType != lookupType) {
+ return false;
+ }
+ return true;
+}
+
+enum class ObjectLookupResult {
+ Failure,
+ Object,
+ Fallback
+};
+
+static ObjectLookupResult initObjectLookup(
const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
{
QV4::Scope scope(aotContext->engine->handle());
@@ -855,7 +954,7 @@ static bool initObjectLookup(
QQmlData *ddata = QQmlData::get(object, true);
Q_ASSERT(ddata);
if (ddata->isQueuedForDeletion)
- return false;
+ return ObjectLookupResult::Failure;
QQmlPropertyData *property;
if (!ddata->propertyCache) {
@@ -865,41 +964,36 @@ static bool initObjectLookup(
name.getPointer(), object, aotContext->qmlContext);
}
- if (!property)
- return false;
-
- const QMetaType propType = property->propType();
- if (!type.isValid()) {
- // If type is invalid, then the calling code depends on the lookup
- // to be set up in order to query the type, via lookupResultMetaType.
- // We cannot verify the type in this case.
- } else if ((type.flags() & QMetaType::IsQmlList) && (propType.flags() & QMetaType::IsQmlList)) {
- // We want to check the value types here, but we cannot easily do it.
- // Internally those are all QObject* lists, though.
- } else if (type.flags() & QMetaType::PointerToQObject) {
- // We accept any base class as type, too
-
- const QMetaObject *typeMetaObject = type.metaObject();
- const QMetaObject *foundMetaObject = propType.metaObject();
- if (!foundMetaObject)
- foundMetaObject = QQmlMetaType::metaObjectForType(propType).metaObject();
-
- while (foundMetaObject) {
- if (foundMetaObject == typeMetaObject)
- break;
- foundMetaObject = foundMetaObject->superClass();
- }
-
- if (!foundMetaObject)
- return false;
- } else if (propType != type) {
- return false;
+ if (!property) {
+ const QMetaObject *metaObject = object->metaObject();
+ if (!metaObject)
+ return ObjectLookupResult::Failure;
+
+ const int coreIndex = metaObject->indexOfProperty(
+ name->toQStringNoThrow().toUtf8().constData());
+ if (coreIndex < 0)
+ return ObjectLookupResult::Failure;
+
+ const QMetaProperty property = metaObject->property(coreIndex);
+ if (!isTypeCompatible(type, property.metaType()))
+ return ObjectLookupResult::Failure;
+
+ l->releasePropertyCache();
+ // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
+ l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
+ l->qobjectFallbackLookup.coreIndex = coreIndex;
+ l->qobjectFallbackLookup.notifyIndex = property.notifySignalIndex();
+ l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
+ return ObjectLookupResult::Fallback;
}
+ if (!isTypeCompatible(type, property->propType()))
+ return ObjectLookupResult::Failure;
+
Q_ASSERT(ddata->propertyCache);
QV4::setupQObjectLookup(l, ddata, property);
- return true;
+ return ObjectLookupResult::Object;
}
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@@ -935,29 +1029,47 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
return false;
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty
- && l->getter != QV4::Lookup::getterQObject) {
- return false;
+ if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::Lookup::getterQObject) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(object, property->coreIndex());
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
- QQmlData::flushPendingBinding(object, property->coreIndex());
- captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ if (l->getter == QV4::Lookup::getterFallback) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(object, coreIndex);
+ captureFallbackProperty(
+ object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+
return true;
}
bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty
- && l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupContextObjectProperty) {
- return false;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
- QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
- captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
- return true;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
+ captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+ return false;
}
QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
@@ -978,6 +1090,14 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
|| l->getter == QV4::QObjectWrapper::lookupAttached) {
return QMetaType::fromType<QObject *>();
+ } else if (l->getter == QV4::Lookup::getterFallback
+ || l->setter == QV4::Lookup::setterFallback
+ || l->qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ return metaObject->property(coreIndex).metaType();
}
return QMetaType();
}
@@ -991,9 +1111,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
QV4::Lookup l;
l.clear();
l.nameIndex = nameIndex;
- if (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
-
- ObjectPropertyResult storeResult;
+ ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
+ switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::Object: {
const QMetaType propType = l.qobjectLookup.propertyData->propType();
if (type == propType) {
storeResult = storeObjectProperty(&l, qmlScopeObject, value);
@@ -1003,20 +1123,38 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
- switch (storeResult) {
- case ObjectPropertyResult::NeedsInit:
- engine->handle()->throwTypeError();
- break;
- case ObjectPropertyResult::Deleted:
- engine->handle()->throwTypeError(
- QStringLiteral("Value is null and could not be converted to an object"));
- break;
- case ObjectPropertyResult::OK:
- break;
- }
l.qobjectLookup.propertyCache->release();
- } else {
+ break;
+ }
+ case ObjectLookupResult::Fallback: {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
+ const QMetaType propType
+ = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
+ if (type == propType) {
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
+ } else {
+ QVariant var(propType);
+ propType.convert(type, value, propType, var.data());
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
+ }
+ break;
+ }
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ return;
+ }
+
+ switch (storeResult) {
+ case ObjectPropertyResult::NeedsInit:
engine->handle()->throwTypeError();
+ break;
+ case ObjectPropertyResult::Deleted:
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ break;
+ case ObjectPropertyResult::OK:
+ break;
}
}
@@ -1181,10 +1319,16 @@ void AOTCompiledContext::initLoadGlobalLookup(uint index) const
bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, qmlScopeObject, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::NeedsInit:
return false;
case ObjectPropertyResult::Deleted:
@@ -1205,12 +1349,22 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
QV4::ExecutionEngine *v4 = engine->handle();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (v4->hasException)
+ if (v4->hasException) {
amendException(v4);
- else if (initObjectLookup(this, l, qmlScopeObject, type))
+ return;
+ }
+
+ switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
+ break;
+ case ObjectLookupResult::Failure:
v4->throwTypeError();
+ break;
+ }
}
bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const
@@ -1351,10 +1505,15 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
if (!object)
return doThrow();
- if (l->getter != QV4::Lookup::getterQObject)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = loadObjectProperty(l, object, target, qmlContext);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = loadFallbackProperty(l, object, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, object, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1374,10 +1533,17 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
l->getter = QV4::Lookup::getterQObject;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->getter = QV4::Lookup::getterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
@@ -1445,10 +1611,15 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
return doThrow();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->setter != QV4::Lookup::setterQObject)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->setter == QV4::Lookup::setterQObject)
+ result = storeObjectProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallback)
+ result = storeFallbackProperty(l, object, value);
+ else
return false;
- switch (storeObjectProperty(l, object, value)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1468,10 +1639,17 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
l->setter = QV4::Lookup::setterQObject;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->setter = QV4::Lookup::setterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 37475dd2ee..565e0a085d 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -250,6 +250,8 @@ The following functions are also on the Qt object.
\qmlproperty object Qt::inputMethod
\since 5.0
+ It is the same as the \l InputMethod singleton.
+
The \c inputMethod object allows access to application's QInputMethod object
and all its properties and slots. See the QInputMethod documentation for
further details.
@@ -260,28 +262,12 @@ The following functions are also on the Qt object.
\since 5.5
The \c styleHints object provides platform-specific style hints and settings.
- See the QStyleHints documentation for further details.
-
- \note The \c styleHints object is only available when using the Qt Quick module.
-
- The following example uses the \c styleHints object to determine whether an
- item should gain focus on mouse press or touch release:
- \code
- import QtQuick 2.4
+ See the \l QStyleHints documentation for further details.
- MouseArea {
- id: button
+ You should access StyleHints via \l Application::styleHints instead.
+ See \l StyleHints for more details.
- onPressed: {
- if (!Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- onReleased: {
- if (Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- }
- \endcode
+ \note The \c styleHints object is only available when using the Qt Quick module.
*/
/*!
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index e70dae5de8..0d8eef6aca 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -40,6 +40,9 @@
#ifndef QQMLCOMPONENT_H
#define QQMLCOMPONENT_H
+#include <QtCore/qvariant.h>
+#include <QtCore/qmap.h>
+
#include <QtQml/qqml.h>
#include <QtQml/qqmlerror.h>
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h
index 7d759dfa82..932d917499 100644
--- a/src/qml/qml/qqmlcontextdata_p.h
+++ b/src/qml/qml/qqmlcontextdata_p.h
@@ -320,9 +320,8 @@ private:
ObjectWasSet
};
- inline ContextGuard() : m_context(nullptr) {}
+ inline ContextGuard() : QQmlGuard<QObject>(&ContextGuard::objectDestroyedImpl, nullptr), m_context(nullptr) {}
inline ContextGuard &operator=(QObject *obj);
- inline void objectDestroyed(QObject *) override;
inline bool wasSet() const;
@@ -333,6 +332,7 @@ private:
}
private:
+ inline static void objectDestroyedImpl(QQmlGuardImpl *);
// Not refcounted, as it always belongs to the QQmlContextData.
QTaggedPointer<QQmlContextData, Tag> m_context;
QQmlNotifier m_bindings;
@@ -459,11 +459,12 @@ QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject
return *this;
}
-void QQmlContextData::ContextGuard::objectDestroyed(QObject *)
+ void QQmlContextData::ContextGuard::objectDestroyedImpl(QQmlGuardImpl *impl)
{
- if (QObject *contextObject = m_context->contextObject()) {
+ auto This = static_cast<QQmlContextData::ContextGuard *>(impl);
+ if (QObject *contextObject = This->m_context->contextObject()) {
if (!QObjectPrivate::get(contextObject)->wasDeleted)
- m_bindings.notify();
+ This->m_bindings.notify();
}
}
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index a88cea55cd..99697f8e1a 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -470,19 +470,32 @@ void QQmlEnginePrivate::init()
// required for the Compiler.
qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
+ qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<int>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<double>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QString>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>("QML", 1);
+ qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>("QML", 1);
+
+ // No need to specifically register those.
+ static_assert(std::is_same_v<QStringList, QList<QString>>);
+ static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
+
+ qRegisterMetaType<QVariant>();
+ qRegisterMetaType<QQmlScriptString>();
+ qRegisterMetaType<QJSValue>();
+ qRegisterMetaType<QQmlComponent::Status>();
+ qRegisterMetaType<QList<QObject*> >();
+ qRegisterMetaType<QQmlBinding*>();
QQmlData::init();
baseModulesUninitialized = false;
}
- qRegisterMetaType<QVariant>();
- qRegisterMetaType<QQmlScriptString>();
- qRegisterMetaType<QJSValue>();
- qRegisterMetaType<QQmlComponent::Status>();
- qRegisterMetaType<QList<QObject*> >();
- qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlBinding*>();
-
q->handle()->setQmlEngine(q);
rootContext = new QQmlContext(q,true);
@@ -1310,9 +1323,10 @@ void QQmlData::destroyed(QObject *object)
ownContext.reset();
while (guards) {
- QQmlGuard<QObject> *guard = static_cast<QQmlGuard<QObject> *>(guards);
- *guard = (QObject *)nullptr;
- guard->objectDestroyed(object);
+ auto *guard = guards;
+ guard->setObject(nullptr);
+ if (guard->objectDestroyed)
+ guard->objectDestroyed(guard);
}
disconnectNotifiers();
@@ -1945,6 +1959,8 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
\sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
*/
+void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
+
QT_END_NAMESPACE
#include "moc_qqmlengine.cpp"
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index e36af7f133..9c167b684b 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE
class QQmlGuardImpl
{
public:
+ using ObjectDestroyedFn = void(*)(QQmlGuardImpl *);
+
inline QQmlGuardImpl();
inline QQmlGuardImpl(QObject *);
inline QQmlGuardImpl(const QQmlGuardImpl &);
@@ -67,70 +69,86 @@ public:
QObject *o = nullptr;
QQmlGuardImpl *next = nullptr;
QQmlGuardImpl **prev = nullptr;
+ ObjectDestroyedFn objectDestroyed = nullptr;
inline void addGuard();
inline void remGuard();
+
+ inline void setObject(QObject *g);
+ bool isNull() const noexcept { return !o; }
};
class QObject;
template<class T>
-class QQmlGuard : private QQmlGuardImpl
+class QQmlGuard : protected QQmlGuardImpl
{
friend class QQmlData;
public:
inline QQmlGuard();
+ inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
inline QQmlGuard(T *);
inline QQmlGuard(const QQmlGuard<T> &);
- inline virtual ~QQmlGuard();
inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o);
inline QQmlGuard<T> &operator=(T *);
- inline T *object() const;
- inline void setObject(T *g);
-
- inline bool isNull() const
- { return !o; }
+ T *object() const noexcept { return static_cast<T *>(o); }
+ void setObject(T *g) { QQmlGuardImpl::setObject(g); }
- inline T* operator->() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
- inline T& operator*() const
- { return *static_cast<T*>(const_cast<QObject*>(o)); }
- inline operator T*() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
- inline T* data() const
- { return static_cast<T*>(const_cast<QObject*>(o)); }
+ using QQmlGuardImpl::isNull;
-protected:
- virtual void objectDestroyed(T *) {}
+ T *operator->() const noexcept { return object(); }
+ T &operator*() const { return *object(); }
+ operator T *() const noexcept { return object(); }
+ T *data() const noexcept { return object(); }
};
+/* used in QQmlStrongJSQObjectReference to indicate that the
+ * object has JS ownership
+ * We save it in objectDestroyFn to save space
+ * (implemented in qqmlengine.cpp)
+ */
+void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
+
template <typename T>
-class QQmlStrongJSQObjectReference : public QQmlGuard<T>
+class QQmlStrongJSQObjectReference : protected QQmlGuardImpl
{
public:
- void setObject(T *o, QObject *parent) {
- T *old = this->object();
- if (o == old)
+ T *object() const noexcept { return static_cast<T *>(o); }
+
+ using QQmlGuardImpl::isNull;
+
+ T *operator->() const noexcept { return object(); }
+ T &operator*() const { return *object(); }
+ operator T *() const noexcept { return object(); }
+ T *data() const noexcept { return object(); }
+
+ void setObject(T *obj, QObject *parent) {
+ T *old = object();
+ if (obj == old)
return;
- if (m_jsOwnership && old && old->parent() == parent)
+ if (hasJsOwnership() && old && old->parent() == parent)
QQml_setParent_noEvent(old, nullptr);
- this->QQmlGuard<T>::operator=(o);
+ QQmlGuardImpl::setObject(obj);
- if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) {
- m_jsOwnership = true;
- QQml_setParent_noEvent(o, parent);
+ if (obj && !obj->parent() && !QQmlData::keepAliveDuringGarbageCollection(obj)) {
+ setJsOwnership(true);
+ QQml_setParent_noEvent(obj, parent);
} else {
- m_jsOwnership = false;
+ setJsOwnership(false);
}
}
private:
- using QQmlGuard<T>::setObject;
- using QQmlGuard<T>::operator=;
- bool m_jsOwnership = false;
+ bool hasJsOwnership() {
+ return objectDestroyed == hasJsOwnershipIndicator;
+ }
+
+ void setJsOwnership(bool itHasOwnership) {
+ objectDestroyed = itHasOwnership ? hasJsOwnershipIndicator : nullptr;
+ }
};
QT_END_NAMESPACE
@@ -149,8 +167,15 @@ QQmlGuardImpl::QQmlGuardImpl(QObject *g)
if (o) addGuard();
}
+/*
+ \internal
+ Copying a QQmlGuardImpl leaves the old one in the intrinsic linked list of guards.
+ The fresh copy does not contain the list pointer of the existing guard; instead
+ only the object and objectDestroyed pointers are copied, and if there is an object
+ we add the new guard to the object's list of guards.
+ */
QQmlGuardImpl::QQmlGuardImpl(const QQmlGuardImpl &g)
-: o(g.o)
+: o(g.o), objectDestroyed(g.objectDestroyed)
{
if (o) addGuard();
}
@@ -191,25 +216,28 @@ QQmlGuard<T>::QQmlGuard()
}
template<class T>
-QQmlGuard<T>::QQmlGuard(T *g)
-: QQmlGuardImpl(g)
+QQmlGuard<T>::QQmlGuard(ObjectDestroyedFn objDestroyed, T *obj)
+ : QQmlGuardImpl(obj)
{
+ objectDestroyed = objDestroyed;
}
template<class T>
-QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g)
+QQmlGuard<T>::QQmlGuard(T *g)
: QQmlGuardImpl(g)
{
}
template<class T>
-QQmlGuard<T>::~QQmlGuard()
+QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g)
+: QQmlGuardImpl(g)
{
}
template<class T>
QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g)
{
+ objectDestroyed = g.objectDestroyed;
setObject(g.object());
return *this;
}
@@ -217,18 +245,15 @@ QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g)
template<class T>
QQmlGuard<T> &QQmlGuard<T>::operator=(T *g)
{
+ /* this does not touch objectDestroyed, as operator= is only a convenience
+ * for setObject. All logic involving objectDestroyed is (sub-)class specific
+ * and remains unaffected.
+ */
setObject(g);
return *this;
}
-template<class T>
-T *QQmlGuard<T>::object() const
-{
- return static_cast<T *>(o);
-}
-
-template<class T>
-void QQmlGuard<T>::setObject(T *g)
+void QQmlGuardImpl::setObject(QObject *g)
{
if (g != o) {
if (prev) remGuard();
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index f51fc6dc22..2eb756be1f 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1397,6 +1397,18 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
const QString& uri, const QString &prefix, QTypeRevision version, uint flags,
QQmlImportDatabase *database, QList<QQmlError> *errors)
{
+ if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
+ QQmlError error;
+ const QString fix = uri.startsWith(Slash) ? QLatin1String("file:") + uri
+ : QLatin1String("qrc") + uri;
+ error.setDescription(QQmlImportDatabase::tr(
+ "\"%1\" is not a valid import URL. "
+ "You can pass relative paths or URLs with schema, but not "
+ "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
Q_ASSERT(errors);
QQmlImportNamespace *nameSpace = importNamespace(prefix);
@@ -1670,7 +1682,7 @@ void QQmlImports::setDesignerSupportRequired(bool b)
designerSupportRequired = b;
}
-static QStringList parseEnvImportPath(const QString &envImportPath)
+static QStringList parseEnvPath(const QString &envImportPath)
{
if (QDir::listSeparator() == u':') {
// Double colons are interpreted as separator + resource path.
@@ -1710,7 +1722,7 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
auto addEnvImportPath = [this](const char *var) {
if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) {
- const QStringList paths = parseEnvImportPath(qEnvironmentVariable(var));
+ const QStringList paths = parseEnvPath(qEnvironmentVariable(var));
for (int ii = paths.count() - 1; ii >= 0; --ii)
addImportPath(paths.at(ii));
}
@@ -1722,15 +1734,19 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
addImportPath(QCoreApplication::applicationDirPath());
+
+ auto addEnvPluginPath = [this](const char *var) {
+ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) {
+ const QStringList paths = parseEnvPath(qEnvironmentVariable(var));
+ for (int ii = paths.count() - 1; ii >= 0; --ii)
+ addPluginPath(paths.at(ii));
+ }
+ };
+
+ addEnvPluginPath("QML_PLUGIN_PATH");
#if defined(Q_OS_ANDROID)
addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml"));
- if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) {
- const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH");
- QLatin1Char pathSep(':');
- QStringList paths = envImportPath.split(pathSep, Qt::SkipEmptyParts);
- for (int ii = paths.count() - 1; ii >= 0; --ii)
- addPluginPath(paths.at(ii));
- }
+ addEnvPluginPath("QT_BUNDLED_LIBS_PATH");
#elif defined(Q_OS_MACOS)
// Add the main bundle's Resources/qml directory as an import path, so that QML modules are
// found successfully when running the app from its build dir.
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 3589dce3f0..1794a27a9d 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -703,7 +703,7 @@ ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const QV4::FunctionObject
LOCALE_STRING_PROPERTY(name)
LOCALE_STRING_PROPERTY(nativeLanguageName)
-LOCALE_STRING_PROPERTY(nativeCountryName)
+QT_IGNORE_DEPRECATIONS(LOCALE_STRING_PROPERTY(nativeCountryName))
LOCALE_STRING_PROPERTY(decimalPoint)
LOCALE_STRING_PROPERTY(groupSeparator)
LOCALE_STRING_PROPERTY(percent)
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index fb0e2af578..125693f3b6 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -47,10 +47,6 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qv4executablecompilationunit_p.h>
-#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
-#endif
-
#include <QtCore/qcoreapplication.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
@@ -257,20 +253,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_PROPERTY
- for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
- QMetaProperty property = mo->property(ii);
-
- int otherIndex = ignoreEnd->indexOfProperty(property.name());
- if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
- builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void"));
- // Skip
- } else {
- builder.addProperty(property);
- }
- }
-
- // Clone Q_METHODS
+ // Clone Q_METHODS - do this first to avoid duplicating the notify signals.
for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) {
QMetaMethod method = mo->method(ii);
@@ -294,6 +277,19 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
m.setAccess(QMetaMethod::Private);
}
+ // Clone Q_PROPERTY
+ for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
+ QMetaProperty property = mo->property(ii);
+
+ int otherIndex = ignoreEnd->indexOfProperty(property.name());
+ if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
+ builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void"));
+ // Skip
+ } else {
+ builder.addProperty(property);
+ }
+ }
+
// Clone Q_ENUMS
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
@@ -1718,17 +1714,7 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
case QMetaType::QEasingCurve:
return &QQmlEasingValueType::staticMetaObject;
#endif
-#if QT_CONFIG(qml_itemmodel)
- case QMetaType::QModelIndex:
- return &QQmlModelIndexValueType::staticMetaObject;
- case QMetaType::QPersistentModelIndex:
- return &QQmlPersistentModelIndexValueType::staticMetaObject;
-#endif
default:
-#if QT_CONFIG(qml_itemmodel)
- if (metaType == QMetaType::fromType<QItemSelectionRange>())
- return &QQmlItemSelectionRangeValueType::staticMetaObject;
-#endif
break;
}
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index ae227109bb..8b905c2351 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -42,6 +42,7 @@
#include <private/qqmldata_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <qdebug.h>
+#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp
index 7723630e1d..fbb691c48e 100644
--- a/src/qml/qml/qqmlpluginimporter.cpp
+++ b/src/qml/qml/qqmlpluginimporter.cpp
@@ -507,7 +507,7 @@ bool QQmlPluginImporter::populatePluginDataVector(QVector<StaticPluginData> &res
QObject *instance = plugin.instance();
if (qobject_cast<QQmlEngineExtensionPlugin *>(instance)
|| qobject_cast<QQmlExtensionPlugin *>(instance)) {
- QJsonArray metaTagsUriList = plugin.metaData().value(
+ const QJsonArray metaTagsUriList = plugin.metaData().value(
QStringLiteral("uri")).toArray();
if (metaTagsUriList.isEmpty()) {
if (errors) {
@@ -523,7 +523,7 @@ bool QQmlPluginImporter::populatePluginDataVector(QVector<StaticPluginData> &res
return false;
}
// A plugin can be set up to handle multiple URIs, so go through the list:
- for (const QJsonValueRef metaTagUri : metaTagsUriList) {
+ for (const QJsonValueConstRef metaTagUri : metaTagsUriList) {
if (versionUris.contains(metaTagUri.toString())) {
result.append({ plugin, metaTagsUriList });
break;
@@ -585,8 +585,8 @@ QTypeRevision QQmlPluginImporter::importPlugins() {
return QTypeRevision();
for (const QString &versionUri : versionUris) {
- for (StaticPluginData &pair : pluginPairs) {
- for (QJsonValueRef metaTagUri : pair.uriList) {
+ for (const StaticPluginData &pair : qAsConst(pluginPairs)) {
+ for (const QJsonValueConstRef metaTagUri : pair.uriList) {
if (versionUri == metaTagUri.toString()) {
staticPluginsFound++;
QObject *instance = pair.plugin.instance();
diff --git a/src/qml/qml/qqmlproperty.h b/src/qml/qml/qqmlproperty.h
index 4ab84eb940..dbffce9437 100644
--- a/src/qml/qml/qqmlproperty.h
+++ b/src/qml/qml/qqmlproperty.h
@@ -40,6 +40,8 @@
#ifndef QQMLPROPERTY_H
#define QQMLPROPERTY_H
+#include <QtCore/qstring.h>
+#include <QtCore/qhashfunctions.h>
#include <QtQml/qtqmlglobal.h>
#include <QtCore/qmetaobject.h>
#include <QtQml/qqmlregistration.h>
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 66e83c1655..035098d4c5 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1256,35 +1256,41 @@ bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &m
return true;
}
-QByteArray QQmlPropertyCache::checksum(bool *ok)
+QByteArray QQmlPropertyCache::checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const
{
- if (!_checksum.isEmpty()) {
+ auto it = checksums->constFind(quintptr(this));
+ if (it != checksums->constEnd()) {
*ok = true;
- return _checksum;
+ return *it;
}
// Generate a checksum on the meta-object data only on C++ types.
if (!_metaObject || _metaObject.isShared()) {
*ok = false;
- return _checksum;
+ return QByteArray();
}
QCryptographicHash hash(QCryptographicHash::Md5);
if (_parent) {
- hash.addData(_parent->checksum(ok));
+ hash.addData(_parent->checksum(checksums, ok));
if (!*ok)
return QByteArray();
}
- if (!addToHash(hash, *createMetaObject())) {
+ if (!addToHash(hash, *_metaObject)) {
*ok = false;
return QByteArray();
}
- _checksum = hash.result();
- *ok = !_checksum.isEmpty();
- return _checksum;
+ const QByteArray result = hash.result();
+ if (result.isEmpty()) {
+ *ok = false;
+ } else {
+ *ok = true;
+ checksums->insert(quintptr(this), result);
+ }
+ return result;
}
/*! \internal
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 68830ba716..4c10de7823 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -247,7 +247,7 @@ public:
static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
- QByteArray checksum(bool *ok);
+ QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const;
QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; }
void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; }
@@ -333,7 +333,6 @@ private:
QByteArray _listPropertyAssignBehavior;
QString _defaultPropertyName;
QQmlPropertyCacheMethodArguments *argumentsCache = nullptr;
- QByteArray _checksum;
int methodIndexCacheStart = 0;
int signalHandlerIndexCacheStart = 0;
int _jsFactoryMethodIndex = -1;
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index aa3be1cabf..448bae3cc5 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -253,7 +253,8 @@ void QQmlTypeData::createTypeAndPropertyCaches(
}
static bool addTypeReferenceChecksumsToHash(
- const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash)
+ const QList<QQmlTypeData::TypeReference> &typeRefs,
+ QHash<quintptr, QByteArray> *checksums, QCryptographicHash *hash)
{
for (const auto &typeRef: typeRefs) {
if (typeRef.typeData) {
@@ -262,7 +263,7 @@ static bool addTypeReferenceChecksumsToHash(
} else if (typeRef.type.isValid()) {
const auto propertyCache = QQmlMetaType::propertyCache(typeRef.type.metaObject());
bool ok = false;
- hash->addData(propertyCache->checksum(&ok));
+ hash->addData(propertyCache->checksum(checksums, &ok));
if (!ok)
return false;
}
@@ -420,8 +421,9 @@ void QQmlTypeData::done()
const auto dependencyHasher = [&resolvedTypeCache, this]() {
QCryptographicHash hash(QCryptographicHash::Md5);
- return (resolvedTypeCache.addToHash(&hash)
- && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash))
+ return (resolvedTypeCache.addToHash(&hash, typeLoader()->checksumCache())
+ && ::addTypeReferenceChecksumsToHash(
+ m_compositeSingletons, typeLoader()->checksumCache(), &hash))
? hash.result()
: QByteArray();
};
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 59c9f858f6..b41f15a7c2 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1220,6 +1220,7 @@ void QQmlTypeLoader::clearCache()
m_qmldirCache.clear();
m_importDirCache.clear();
m_importQmlDirCache.clear();
+ m_checksumCache.clear();
QQmlMetaType::freeUnusedTypesAndCaches();
}
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index eb6e549911..10f96df735 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -78,6 +78,7 @@ class Q_QML_PRIVATE_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
+ using ChecksumCache = QHash<quintptr, QByteArray>;
enum Mode { PreferSynchronous, Asynchronous, Synchronous };
class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob
@@ -141,6 +142,8 @@ public:
~QQmlTypeLoader();
QQmlImportDatabase *importDatabase() const;
+ ChecksumCache *checksumCache() { return &m_checksumCache; }
+ const ChecksumCache *checksumCache() const { return &m_checksumCache; }
static QUrl normalize(const QUrl &unNormalizedUrl);
@@ -247,6 +250,7 @@ private:
QmldirCache m_qmldirCache;
ImportDirCache m_importDirCache;
ImportQmlDirCache m_importQmlDirCache;
+ ChecksumCache m_checksumCache;
template<typename Loader>
void doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode);
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 93abe3c83a..ef41d81868 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -165,31 +165,28 @@ static void list_removeLast(QQmlListProperty<QObject> *prop)
}
QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr()
- : QQmlGuard<QObject>(nullptr), m_target(nullptr), m_index(-1)
+ : QQmlGuard<QObject>(QQmlVMEVariantQObjectPtr::objectDestroyedImpl, nullptr), m_target(nullptr), m_index(-1)
{
}
-QQmlVMEVariantQObjectPtr::~QQmlVMEVariantQObjectPtr()
+void QQmlVMEVariantQObjectPtr::objectDestroyedImpl(QQmlGuardImpl *guard)
{
-}
-
-void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
-{
- if (!m_target || QQmlData::wasDeleted(m_target->object))
+ auto This = static_cast<QQmlVMEVariantQObjectPtr *>(guard);
+ if (!This->m_target || QQmlData::wasDeleted(This->m_target->object))
return;
- if (m_index >= 0) {
- QV4::ExecutionEngine *v4 = m_target->propertyAndMethodStorage.engine();
+ if (This->m_index >= 0) {
+ QV4::ExecutionEngine *v4 = This->m_target->propertyAndMethodStorage.engine();
if (v4) {
QV4::Scope scope(v4);
- QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
+ QV4::Scoped<QV4::MemberData> sp(scope, This->m_target->propertyAndMethodStorage.value());
if (sp) {
- QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index };
+ QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + This->m_index };
index.set(v4, QV4::Value::nullValue());
}
}
- m_target->activate(m_target->object, m_target->methodOffset() + m_index, nullptr);
+ This->m_target->activate(This->m_target->object, This->m_target->methodOffset() + This->m_index, nullptr);
}
}
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index d8ae670597..e7ddf4d144 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -79,13 +79,14 @@ class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
public:
inline QQmlVMEVariantQObjectPtr();
- inline ~QQmlVMEVariantQObjectPtr() override;
- inline void objectDestroyed(QObject *) override;
inline void setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index);
QQmlVMEMetaObject *m_target;
int m_index;
+
+private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard);
};
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index bb9036582e..3bc960291a 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -241,6 +241,23 @@ bool QQmlDirParser::parse(const QString &source)
"not %1.").arg(sections[1]));
continue;
}
+ } else if (sections[0] == QLatin1String("default")) {
+ if (sectionCount < 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("default directive requires further "
+ "arguments, but none were provided."));
+ continue;
+ }
+ if (sections[1] == QLatin1String("import")) {
+ if (!readImport(sections + 1, sectionCount - 1,
+ Import::Flags({ Import::Optional, Import::OptionalDefault })))
+ continue;
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("only optional imports can have a a defaultl, "
+ "not %1.")
+ .arg(sections[1]));
+ }
} else if (sections[0] == QLatin1String("classname")) {
if (sectionCount < 2) {
reportError(lineNumber, 0,
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index 6367cae141..1a9fa6ff2b 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -134,9 +134,11 @@ public:
struct Import
{
enum Flag {
- Default = 0x0,
- Auto = 0x1, // forward the version of the importing module
- Optional = 0x2 // is not automatically imported but only a tooling hint
+ Default = 0x0,
+ Auto = 0x1, // forward the version of the importing module
+ Optional = 0x2, // is not automatically imported but only a tooling hint
+ OptionalDefault =
+ 0x4, // tooling hint only, denotes this entry should be imported by tooling
};
Q_DECLARE_FLAGS(Flags, Flag)
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 25a0d130a6..59d8bf8a98 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -8,7 +8,8 @@ qt_internal_add_module(QmlCompilerPrivate
SOURCES
qcoloroutput_p.h qcoloroutput.cpp
qdeferredpointer_p.h
- qqmljsannotation_p.h qqmljsannotation.cpp
+ qqmljsannotation.cpp qqmljsannotation_p.h
+ qqmljsbasicblocks.cpp qqmljsbasicblocks_p.h
qqmljscodegenerator.cpp qqmljscodegenerator_p.h
qqmljscompilepass_p.h
qqmljscompiler.cpp qqmljscompiler_p.h
@@ -17,7 +18,7 @@ qt_internal_add_module(QmlCompilerPrivate
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
qqmljslogger_p.h qqmljslogger.cpp
- qqmljsmetatypes_p.h
+ qqmljsmetatypes_p.h qqmljsmetatypes.cpp
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
qqmljsscope.cpp qqmljsscope_p.h
diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h
index 9bf67fb27f..2da17b3dd9 100644
--- a/src/qmlcompiler/qdeferredpointer_p.h
+++ b/src/qmlcompiler/qdeferredpointer_p.h
@@ -120,6 +120,26 @@ public:
return !(a == b);
}
+ friend bool operator<(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data < b.m_data;
+ }
+
+ friend bool operator<=(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data <= b.m_data;
+ }
+
+ friend bool operator>(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data > b.m_data;
+ }
+
+ friend bool operator>=(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_data >= b.m_data;
+ }
+
template <typename U>
friend bool operator==(const QDeferredSharedPointer &a, const QSharedPointer<U> &b)
{
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
new file mode 100644
index 0000000000..ebd0458897
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "qqmljsbasicblocks_p.h"
+
+QT_BEGIN_NAMESPACE
+
+template<typename Container>
+void deduplicate(Container &container)
+{
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+}
+
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
+ const Function *function,
+ const InstructionAnnotations &annotations)
+{
+ m_annotations = annotations;
+ m_basicBlocks.insert_or_assign(0, BasicBlock());
+
+ const QByteArray byteCode = function->code;
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ if (m_hadBackJumps) {
+ // We may have missed some connections between basic blocks if there were back jumps.
+ // Fill them in via a second pass.
+ reset();
+ decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
+ for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
+ deduplicate(it->second.jumpOrigins);
+ }
+
+ populateBasicBlocks();
+ populateReaderLocations();
+ adjustTypes();
+ return std::move(m_annotations);
+}
+
+QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type)
+{
+ auto it = m_basicBlocks.find(currentInstructionOffset());
+ if (it != m_basicBlocks.end()) {
+ if (!m_skipUntilNextLabel && it != m_basicBlocks.begin())
+ it->second.jumpOrigins.append((--it)->first);
+ m_skipUntilNextLabel = false;
+ } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
+ return SkipInstruction;
+ }
+
+ return ProcessInstruction;
+}
+
+void QQmlJSBasicBlocks::endInstruction(QV4::Moth::Instr::Type)
+{
+}
+
+void QQmlJSBasicBlocks::generate_Jump(int offset)
+{
+ processJump(offset, Unconditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpTrue(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpFalse(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNoException(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset)
+{
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_Ret()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::generate_ThrowException()
+{
+ m_skipUntilNextLabel = true;
+}
+
+void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
+{
+ if (offset < 0)
+ m_hadBackJumps = true;
+ const int jumpTarget = absoluteOffset(offset);
+ Q_ASSERT(!m_basicBlocks.isEmpty());
+ auto currentBlock = m_basicBlocks.lower_bound(currentInstructionOffset());
+ if (currentBlock == m_basicBlocks.end() || currentBlock->first != currentInstructionOffset())
+ --currentBlock;
+ currentBlock->second.jumpTarget = jumpTarget;
+ currentBlock->second.jumpIsUnconditional = (mode == Unconditional);
+ m_basicBlocks[jumpTarget].jumpOrigins.append(currentInstructionOffset());
+ if (mode == Unconditional)
+ m_skipUntilNextLabel = true;
+ else
+ m_basicBlocks[nextInstructionOffset()].jumpOrigins.append(currentInstructionOffset());
+}
+
+template<typename ContainerA, typename ContainerB>
+static bool containsAny(const ContainerA &container, const ContainerB &elements)
+{
+ for (const auto &element : elements) {
+ if (container.contains(element))
+ return true;
+ }
+ return false;
+}
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
+class NewFlatMap
+{
+public:
+ using OriginalFlatMap = QFlatMap<Key, T, Compare, KeyContainer, MappedContainer>;
+
+ void appendOrdered(const typename OriginalFlatMap::iterator &i) {
+ keys.append(i.key());
+ values.append(i.value());
+ }
+
+ OriginalFlatMap take() {
+ OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
+ keys.clear();
+ values.clear();
+ return result;
+ }
+
+private:
+ typename OriginalFlatMap::key_container_type keys;
+ typename OriginalFlatMap::mapped_container_type values;
+};
+
+void QQmlJSBasicBlocks::populateReaderLocations()
+{
+ using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
+
+ bool erasedReaders = false;
+ auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
+ auto reader = m_readerLocations.find(it.key());
+ if (reader != m_readerLocations.end() && reader->readers.isEmpty()) {
+
+ // void the output, rather than deleting it. We still need its variant.
+ m_typeResolver->adjustTrackedType(
+ it->second.changedRegister.storedType(),
+ m_typeResolver->voidType());
+ m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(it->second.changedRegister),
+ m_typeResolver->voidType());
+ m_readerLocations.erase(reader);
+
+ // If it's not a label and has no side effects, we can drop the instruction.
+ if (!it->second.hasSideEffects && m_basicBlocks.find(it.key()) == m_basicBlocks.end()) {
+ if (!it->second.readRegisters.isEmpty()) {
+ it->second.readRegisters.clear();
+ erasedReaders = true;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ NewInstructionAnnotations newAnnotations;
+ for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
+ writeIt != writeEnd; ++writeIt) {
+ const int writtenRegister = writeIt->second.changedRegisterIndex;
+ if (writtenRegister == InvalidRegister) {
+ newAnnotations.appendOrdered(writeIt);
+ continue;
+ }
+
+ RegisterAccess &access = m_readerLocations[writeIt.key()];
+ if (writeIt->second.changedRegister.isConversion()) {
+ // If it's a conversion, we have to check for all readers of the conversion origins.
+ // This happens at jump targets where different types are merged. A StoreReg or similar
+ // instruction must be optimized out if none of the types it can hold is read anymore.
+ access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
+ } else {
+ access.trackedTypes.append(
+ m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
+ }
+
+ auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
+ if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
+ --blockIt;
+
+ QList<int> blocks = { blockIt->first };
+ QList<int> processedBlocks;
+ bool isFirstBlock = true;
+
+ while (!blocks.isEmpty()) {
+ auto nextBlock = m_basicBlocks.find(blocks.takeLast());
+ auto currentBlock = nextBlock++;
+
+ if (isFirstBlock
+ || containsAny(currentBlock->second.readRegisters, access.trackedTypes)) {
+ const auto blockEnd = (nextBlock == m_basicBlocks.end())
+ ? m_annotations.end()
+ : m_annotations.find(nextBlock->first);
+
+ auto blockInstr = isFirstBlock
+ ? (writeIt + 1)
+ : m_annotations.find(currentBlock->first);
+ for (; blockInstr != blockEnd; ++blockInstr) {
+ for (auto readIt = blockInstr->second.readRegisters.constBegin(),
+ end = blockInstr->second.readRegisters.constEnd();
+ readIt != end; ++readIt) {
+ Q_ASSERT(readIt->second.isConversion());
+ if (containsAny(readIt->second.conversionOrigins(), access.trackedTypes))
+ access.readers[blockInstr.key()] = readIt->second.conversionResult();
+ }
+ }
+ }
+
+ if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()
+ && !processedBlocks.contains(nextBlock->first)) {
+ blocks.append(nextBlock->first);
+ }
+
+ const int jumpTarget = currentBlock->second.jumpTarget;
+ if (jumpTarget != -1 && !processedBlocks.contains(jumpTarget))
+ blocks.append(jumpTarget);
+
+ // We can re-enter the first block from the beginning.
+ // We will then find any reads before the write we're currently examining.
+ if (isFirstBlock)
+ isFirstBlock = false;
+ else
+ processedBlocks.append(currentBlock->first);
+ }
+
+ if (!eraseDeadStore(writeIt))
+ newAnnotations.appendOrdered(writeIt);
+ }
+ m_annotations = newAnnotations.take();
+
+ while (erasedReaders) {
+ erasedReaders = false;
+
+ for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
+ InstructionAnnotation &instruction = it->second;
+ if (instruction.changedRegisterIndex < InvalidRegister) {
+ newAnnotations.appendOrdered(it);
+ continue;
+ }
+
+ auto readers = m_readerLocations.find(it.key());
+ if (readers != m_readerLocations.end()) {
+ for (auto it = readers->readers.begin(); it != readers->readers.end();) {
+ if (m_annotations.contains(it.key()))
+ ++it;
+ else
+ it = readers->readers.erase(it);
+ }
+ }
+
+ if (!eraseDeadStore(it))
+ newAnnotations.appendOrdered(it);
+ }
+
+ m_annotations = newAnnotations.take();
+ }
+}
+
+void QQmlJSBasicBlocks::adjustTypes()
+{
+ using NewVirtualRegisters = NewFlatMap<int, QQmlJSRegisterContent>;
+
+ for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
+ // There is always one first occurrence of any tracked type. Conversions don't change
+ // the type.
+ if (it->trackedTypes.length() != 1)
+ continue;
+
+ m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->readers.values());
+ }
+
+ const auto transformRegister = [&](QQmlJSRegisterContent &content) {
+ m_typeResolver->adjustTrackedType(
+ content.storedType(),
+ m_typeResolver->storedType(m_typeResolver->containedType(content)));
+ };
+
+ NewVirtualRegisters newRegisters;
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
+ if (i->second.changedRegisterIndex != InvalidRegister)
+ transformRegister(i->second.changedRegister);
+ for (auto content : i->second.typeConversions) {
+ QQmlJSScope::ConstPtr conversionResult = content.second.conversionResult();
+ const auto conversionOrigins = content.second.conversionOrigins();
+ QQmlJSScope::ConstPtr newResult;
+ for (const auto &origin : conversionOrigins)
+ newResult = m_typeResolver->merge(newResult, origin);
+ m_typeResolver->adjustTrackedType(conversionResult, newResult);
+ transformRegister(content.second);
+ }
+ i->second.typeConversions = newRegisters.take();
+ }
+}
+
+void QQmlJSBasicBlocks::populateBasicBlocks()
+{
+ for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
+ blockNext != blockEnd;) {
+
+ const auto blockIt = blockNext++;
+ BasicBlock &block = blockIt->second;
+ QList<QQmlJSScope::ConstPtr> writtenRegisters;
+
+ const auto instrEnd = (blockNext == blockEnd)
+ ? m_annotations.end()
+ : m_annotations.find(blockNext->first);
+ for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
+ const InstructionAnnotation &instruction = instrIt->second;
+ for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
+ it != end; ++it) {
+ Q_ASSERT(it->second.isConversion());
+ for (const QQmlJSScope::ConstPtr &origin : it->second.conversionOrigins()) {
+ if (!writtenRegisters.contains(origin))
+ block.readRegisters.append(origin);
+ }
+ }
+
+ // If it's just a renaming, the type has existed in a different register before.
+ if (instruction.changedRegisterIndex != InvalidRegister && !instruction.isRename) {
+ writtenRegisters.append(m_typeResolver->trackedContainedType(
+ instruction.changedRegister));
+ }
+ }
+
+ deduplicate(block.readRegisters);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
new file mode 100644
index 0000000000..76f3216ce8
--- /dev/null
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 QQMLJSBASICBLOCKS_P_H
+#define QQMLJSBASICBLOCKS_P_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 <private/qqmljscompilepass_p.h>
+#include <private/qflatmap_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlJSBasicBlocks : public QQmlJSCompilePass
+{
+public:
+ struct BasicBlock {
+ QList<int> jumpOrigins;
+ QList<QQmlJSScope::ConstPtr> readRegisters;
+ int jumpTarget = -1;
+ bool jumpIsUnconditional = false;
+ };
+
+ QQmlJSBasicBlocks(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ {
+ }
+
+ ~QQmlJSBasicBlocks() = default;
+
+ InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations);
+
+private:
+ struct RegisterAccess
+ {
+ QList<QQmlJSScope::ConstPtr> trackedTypes;
+ QHash<int, QQmlJSScope::ConstPtr> readers;
+ };
+
+ QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override;
+ void endInstruction(QV4::Moth::Instr::Type type) override;
+
+ void generate_Jump(int offset) override;
+ void generate_JumpTrue(int offset) override;
+ void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
+
+ void generate_Ret() override;
+ void generate_ThrowException() override;
+
+ enum JumpMode { Unconditional, Conditional };
+ void processJump(int offset, JumpMode mode);
+ void populateBasicBlocks();
+ void populateReaderLocations();
+ void adjustTypes();
+
+ InstructionAnnotations m_annotations;
+ QFlatMap<int, BasicBlock> m_basicBlocks;
+ QHash<int, RegisterAccess> m_readerLocations;
+ bool m_skipUntilNextLabel = false;
+ bool m_hadBackJumps = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSBASICBLOCKS_P_H
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index f665b1ae71..f7fa9ba403 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -34,6 +34,7 @@
#include <private/qqmljsscope_p.h>
#include <private/qqmljsutils_p.h>
#include <private/qv4compilerscanfunctions_p.h>
+#include <private/qduplicatetracker_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
@@ -83,13 +84,26 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite())
+ if (!objectType->isComposite()) {
+ if (objectType->internalName() == u"QObject"_qs
+ || objectType->internalName() == u"QQmlComponent"_qs) {
+ return u'&' + objectType->internalName() + u"::staticMetaObject"_qs;
+ }
return metaTypeFromName(objectType) + u".metaObject()"_qs;
+ }
reject(u"retrieving the metaObject of a composite type without using an instance."_qs);
return QString();
}
+static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
+{
+ return !type.isNull()
+ && !resolver->equals(type, resolver->nullType())
+ && !resolver->equals(type, resolver->emptyListType())
+ && !resolver->equals(type, resolver->voidType());
+}
+
QQmlJSAotFunction QQmlJSCodeGenerator::run(
const Function *function, const InstructionAnnotations *annotations,
QQmlJS::DiagnosticMessage *error)
@@ -98,34 +112,41 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
m_function = function;
m_error = error;
- QSet<QString> registerNames;
- for (const InstructionAnnotation &annotation : *m_annotations) {
- for (auto regIt = annotation.registers.constBegin(),
- regEnd = annotation.registers.constEnd();
- regIt != regEnd;
- ++regIt) {
- int registerIndex = regIt.key();
+ QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> registerNames;
- const QQmlJSScope::ConstPtr seenType = regIt.value().storedType();
- // Don't generate any variables for registers that are initialized with undefined.
- if (seenType.isNull() || seenType == m_typeResolver->voidType())
- continue;
+ auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) {
+ // Don't generate any variables for registers that are initialized with undefined.
+ if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
+ return;
- auto &typesForRegisters = m_registerVariables[registerIndex];
- if (!typesForRegisters.contains(seenType)) {
- QString variableName = u"r%1"_qs.arg(registerIndex);
- if (registerNames.contains(variableName))
- variableName += u'_' + QString::number(typesForRegisters.count());
- registerNames.insert(variableName);
- typesForRegisters[seenType] = variableName;
- }
+ auto &typesForRegisters = m_registerVariables[registerIndex];
+ if (!typesForRegisters.contains(seenType)) {
+ auto &currentRegisterNames = registerNames[registerIndex];
+ QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)];
+ if (name.isEmpty())
+ name = u"r%1_%2"_qs.arg(registerIndex).arg(currentRegisterNames.count());
+ typesForRegisters[seenType] = name;
+ }
+ };
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
+ for (const auto &annotation : *m_annotations) {
+ addVariable(annotation.second.changedRegisterIndex,
+ annotation.second.changedRegister.storedType());
+ for (auto it = annotation.second.typeConversions.begin(),
+ end = annotation.second.typeConversions.end();
+ it != end; ++it) {
+ addVariable(it.key(), it.value().storedType());
}
}
+QT_WARNING_POP
// ensure we have m_labels for loops
for (const auto loopLabel : m_context->labelInfo)
m_labels.insert(loopLabel, u"label_%1"_qs.arg(m_labels.count()));
+ m_state.State::operator=(initialState(function));
const QByteArray byteCode = function->code;
decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
eliminateDeadStores();
@@ -133,11 +154,16 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
QQmlJSAotFunction result;
result.includes.swap(m_includes);
+ QDuplicateTracker<QString> generatedVariables;
for (const auto &registerTypes : qAsConst(m_registerVariables)) {
for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd();
registerTypeIt != end; ++registerTypeIt) {
const QQmlJSScope::ConstPtr storedType = registerTypeIt.key();
+
+ if (generatedVariables.hasSeen(registerTypeIt.value()))
+ continue;
+
result.code += storedType->internalName();
if (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
@@ -153,9 +179,9 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
for (const Section &section : m_sections)
result.code += section.code();
- for (const QQmlJSScope::ConstPtr &argType : qAsConst(function->argumentTypes)) {
- if (argType) {
- result.argumentTypes.append(argType->augmentedInternalName());
+ for (const QQmlJSRegisterContent &argType : qAsConst(function->argumentTypes)) {
+ if (argType.isValid()) {
+ result.argumentTypes.append(argType.storedType()->augmentedInternalName());
} else {
result.argumentTypes.append(u"void"_qs);
}
@@ -184,9 +210,11 @@ QList<QQmlJSCodeGenerator::BasicBlock> QQmlJSCodeGenerator::findBasicBlocks(
const QQmlJSCodeGenerator::Section &section = sections[i];
const QString label = section.label();
if (!label.isEmpty() || currentBlock.jumpMode != JumpMode::None) {
- currentBlock.endSection = i;
- basicBlocks.append(currentBlock);
- currentBlock.beginSection = i;
+ if (currentBlock.beginSection != i) {
+ currentBlock.endSection = i;
+ basicBlocks.append(currentBlock);
+ currentBlock.beginSection = i;
+ }
currentBlock.label = label;
}
currentBlock.jumpMode = section.jumpMode();
@@ -320,6 +348,7 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
usedOnce = true;
}
}
+ requiredRegisters[0][variable] = inUse;
if (!usedOnce) {
registerTypeIt = registerTypes.erase(registerTypeIt);
@@ -332,7 +361,8 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
// Sort the offsets in reverse order so that we can pop from the back
std::sort(toErase.begin(), toErase.end());
- int eraseIndex = toErase.length() - 1;
+ auto end = std::unique(toErase.begin(), toErase.end());
+ int eraseIndex = (end - toErase.begin()) - 1;
for (int i = m_sections.length() - 1; i >= 0 && eraseIndex >= 0; --i) {
if (i != toErase[eraseIndex])
continue;
@@ -361,12 +391,10 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
} while (!toErase.isEmpty() || foundUnknownBlock);
}
-QString QQmlJSCodeGenerator::errorReturnValue() const
+QString QQmlJSCodeGenerator::errorReturnValue()
{
- if (m_function->returnType) {
- return conversion(m_typeResolver->jsPrimitiveType(), m_function->returnType,
- u"QJSPrimitiveValue(QJSPrimitiveUndefined())"_qs);
- }
+ if (m_function->returnType)
+ return conversion(m_typeResolver->voidType(), m_function->returnType, QString());
return QString();
}
@@ -381,29 +409,29 @@ void QQmlJSCodeGenerator::generate_Ret()
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs;
if (!m_state.accumulatorVariableIn.isEmpty()) {
const QString in = use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->varType()) {
+ if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->varType())) {
m_body += u"if (!"_qs + in + u".isValid())\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsPrimitiveType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
m_body += u"if ("_qs + in
+ u".type() == QJSPrimitiveValue::Undefined)\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn.storedType() == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
m_body += u"if ("_qs + in + u".isUndefined())\n"_qs;
m_body += u" "_qs + signalUndefined;
}
m_body += u"return "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_function->returnType, in);
- } else if (m_function->returnType != m_typeResolver->voidType()
- && m_function->returnType->internalName() != u"void"_qs) {
- if (m_function->returnType->internalName().trimmed().endsWith(u'*')
- || m_function->returnType->internalName().trimmed().endsWith(u'&')) {
- setError(u"Not all paths return a value"_qs);
- return;
+ + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in);
+ } else {
+ if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
+ m_typeResolver->voidType())) {
+ m_body += signalUndefined;
}
-
- m_body += signalUndefined;
- m_body += u"return "_qs + m_function->returnType->internalName() + u"()"_qs;
+ m_body += u"return "_qs + conversion(
+ m_state.accumulatorIn().storedType(), m_function->returnType, QString());
}
} else {
m_body += u"return"_qs;
@@ -426,6 +454,19 @@ static QString toNumericString(double value)
return QString::number(i);
}
+ switch (qFpClassify(value)) {
+ case FP_INFINITE: {
+ const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
+ return std::signbit(value) ? (u'-' + inf) : inf;
+ }
+ case FP_NAN:
+ return u"std::numeric_limits<double>::quiet_NaN()"_qs;
+ case FP_ZERO:
+ return std::signbit(value) ? u"-0.0"_qs : u"0"_qs;
+ default:
+ break;
+ }
+
return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
}
@@ -436,9 +477,14 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index)
auto encodedConst = m_jsUnitGenerator->constant(index);
double value = QV4::StaticValue::fromReturnedValue(encodedConst).doubleValue();
m_body += m_state.accumulatorVariableOut;
- m_body += u" = "_qs;
- m_body += toNumericString(value); // ### handle other types
+
+ // ### handle other types?
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ toNumericString(value));
+
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadZero()
@@ -446,8 +492,10 @@ void QQmlJSCodeGenerator::generate_LoadZero()
INJECT_TRACE_INFO(generate_LoadZero);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = 0"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(), u"0"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadTrue()
@@ -455,8 +503,10 @@ void QQmlJSCodeGenerator::generate_LoadTrue()
INJECT_TRACE_INFO(generate_LoadTrue);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = true"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"true"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadFalse()
@@ -464,43 +514,53 @@ void QQmlJSCodeGenerator::generate_LoadFalse()
INJECT_TRACE_INFO(generate_LoadFalse);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = false"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"false"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadNull()
{
INJECT_TRACE_INFO(generate_LoadNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = nullptr"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut().storedType(),
+ u"nullptr"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->nullType());
}
void QQmlJSCodeGenerator::generate_LoadUndefined()
{
INJECT_TRACE_INFO(generate_LoadUndefined);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
- m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
- u"QJSPrimitiveValue()"_qs) + u";\n"_qs;
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->voidType());
}
void QQmlJSCodeGenerator::generate_LoadInt(int value)
{
INJECT_TRACE_INFO(generate_LoadInt);
- Q_ASSERT(m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->intType()));
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QString::number(value);
+ m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ QString::number(value));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
{
INJECT_TRACE_INFO(generate_MoveConst);
- auto var = registerVariable(destTemp);
+ Q_ASSERT(destTemp == m_state.changedRegisterIndex());
+
+ auto var = changedRegisterVariable();
if (var.isEmpty())
return; // Do not load 'undefined'
@@ -508,48 +568,38 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
const auto v4Value = QV4::StaticValue::fromReturnedValue(
m_jsUnitGenerator->constant(constIndex));
- if (v4Value.isNull()) {
- const auto type = registerType(destTemp).storedType();
- m_body += var + u" = "_qs;
- if (type == m_typeResolver->jsPrimitiveType()) {
- m_body += u"QJSPrimitiveNull()"_qs;
- } else if (type == m_typeResolver->jsValueType()) {
- m_body += u"QJSValue(QJSValue::NullValue)"_qs;
- } else if (type == m_typeResolver->varType()) {
- m_body += u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
- } else if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- m_body += u"nullptr"_qs;
- } else {
- setError(u"Cannot load null into %1"_qs.arg(m_state.accumulatorOut.descriptiveName()));
- }
+ const auto changed = m_state.changedRegister().storedType();
+ QQmlJSScope::ConstPtr contained;
+ QString input;
- m_body += u";\n"_qs;
- return;
+ m_body += var + u" = "_qs;
+ if (v4Value.isNull()) {
+ contained = m_typeResolver->nullType();
+ } else if (v4Value.isUndefined()) {
+ contained = m_typeResolver->voidType();
+ } else if (v4Value.isBoolean()) {
+ contained = m_typeResolver->boolType();
+ input = v4Value.booleanValue() ? u"true"_qs : u"false"_qs;
+ } else if (v4Value.isInteger()) {
+ contained = m_typeResolver->intType();
+ input = QString::number(v4Value.int_32());
+ } else if (v4Value.isDouble()) {
+ contained = m_typeResolver->realType();
+ input = toNumericString(v4Value.doubleValue());
+ } else {
+ reject(u"unknown const type"_qs);
}
-
- double value = 0.0f;
- if (v4Value.isInteger() || v4Value.isBoolean())
- value = v4Value.int_32();
- else if (v4Value.isDouble())
- value = v4Value.doubleValue();
-
- m_body += var;
- m_body += u" = "_qs;
- m_body += toNumericString(value);
- m_body += u";\n"_qs;
+ m_body += conversion(contained, changed, input) + u";\n"_qs;
+ generateOutputVariantConversion(contained);
}
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
{
INJECT_TRACE_INFO(generate_LoadReg);
- // We can't emit any code yet for loading undefined...
- // See also generate_LoadUndefined()
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType()))
- return;
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += use(registerVariable(reg));
+ m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg)));
m_body += u";\n"_qs;
}
@@ -557,14 +607,15 @@ void QQmlJSCodeGenerator::generate_StoreReg(int reg)
{
INJECT_TRACE_INFO(generate_StoreReg);
- Q_ASSERT(m_state.accumulatorIn.isValid());
- const QString var = registerVariable(reg);
+ Q_ASSERT(m_state.changedRegisterIndex() == reg);
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ const QString var = changedRegisterVariable();
m_body.setWriteRegister(var);
if (var.isEmpty())
return; // don't store "undefined"
m_body += var;
m_body += u" = "_qs;
- m_body += conversion(m_state.accumulatorIn, registerType(reg),
+ m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -573,11 +624,15 @@ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
{
INJECT_TRACE_INFO(generate_MoveReg);
- const QString destRegName = registerVariable(destReg);
+ Q_ASSERT(m_state.changedRegisterIndex() == destReg);
+ const QString destRegName = changedRegisterVariable();
m_body.setWriteRegister(destRegName);
+ if (destRegName.isEmpty())
+ return; // don't store things we cannot store.
m_body += destRegName;
m_body += u" = "_qs;
- m_body += use(registerVariable(srcReg));
+ m_body += conversion(registerType(srcReg), m_state.changedRegister(),
+ use(registerVariable(srcReg)));
m_body += u";\n"_qs;
}
@@ -619,8 +674,11 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId));
+ m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(),
+ QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->stringType());
}
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
@@ -646,9 +704,11 @@ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadGlobalLookup);
+ AccumulatorConverter registers(this);
+
const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index)
+ u", &"_qs + m_state.accumulatorVariableOut + u", "_qs
- + metaTypeFromType(m_state.accumulatorOut.storedType()) + u')';
+ + metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
const QString initialization = u"aotContext->initLoadGlobalLookup("_qs
+ QString::number(index) + u')';
generateLookup(lookup, initialization);
@@ -658,55 +718,51 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
- if (m_state.accumulatorVariableOut.isEmpty())
- return;
+ AccumulatorConverter registers(this);
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ conversion(
- m_typeResolver->jsValueType(), m_state.accumulatorOut.storedType(),
+ m_typeResolver->jsValueType(), m_state.accumulatorOut().storedType(),
u"aotContext->javaScriptGlobalProperty("_qs + QString::number(nameIndex) + u")")
+ u";\n"_qs;
return;
}
const QString indexString = QString::number(index);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectById) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
const QString lookup = u"aotContext->loadContextIdLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initLoadContextIdLookup("_qs
+ indexString + u')';
generateLookup(lookup, initialization);
return;
}
- const bool isProperty = m_state.accumulatorOut.isProperty();
- const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut.scopeType();
- const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut.storedType();
+ const bool isProperty = m_state.accumulatorOut().isProperty();
+ const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
if (isProperty) {
- m_body += u"{\n"_qs;
-
- const auto lookupType = contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut);
+ const auto lookupType = contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization
= u"aotContext->initLoadScopeObjectPropertyLookup("_qs
+ indexString + u", "_qs
+ lookupType + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if (m_state.accumulatorOut.isType() || m_state.accumulatorOut.isImportNamespace()) {
+ } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
generateTypeLookup(index);
} else {
- Q_UNREACHABLE();
+ reject(u"lookup of %1"_qs.arg(m_state.accumulatorOut().descriptiveName()));
}
}
@@ -726,10 +782,10 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
switch (type.variant()) {
case QQmlJSRegisterContent::ScopeProperty:
case QQmlJSRegisterContent::ExtensionScopeProperty: {
- if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn)) {
+ if (!m_typeResolver->registerContains(m_state.accumulatorIn(), type.property().type())) {
m_body += u"{\n"_qs;
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, type, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), type, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs + contentPointer(type, u"converted"_qs)
@@ -739,9 +795,9 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
} else {
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs
- + contentPointer(m_state.accumulatorIn, use(m_state.accumulatorVariableIn))
+ + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn))
+ u", "_qs
- + contentType(m_state.accumulatorIn, use(m_state.accumulatorVariableIn)) + u')';
+ + contentType(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u')';
m_body += u";\n"_qs;
}
break;
@@ -767,16 +823,18 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseType = registerType(base);
- if (!m_typeResolver->isNumeric(m_state.accumulatorIn) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) {
reject(u"LoadElement with non-list base type or non-numeric arguments"_qs);
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect LoadElement"_qs);
return;
}
+ AccumulatorConverter registers(this);
+
const QString baseName = use(registerVariable(base));
const QString indexName = use(m_state.accumulatorVariableIn);
@@ -789,7 +847,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
+ u" < "_qs + baseName + u".count(&"_qs + baseName
+ u"))\n"_qs;
m_body += u" "_qs + m_state.accumulatorVariableOut + u" = "_qs +
- conversion(elementType, m_state.accumulatorOut,
+ conversion(elementType, m_state.accumulatorOut(),
baseName + u".at(&"_qs + baseName + u", "_qs
+ indexName + u')') + u";\n"_qs;
m_body += u"else\n"_qs;
@@ -811,7 +869,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect StoreElement"_qs);
return;
}
@@ -828,7 +886,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
+ u"))\n"_qs;
m_body += u" "_qs + baseName + u".replace(&"_qs + baseName
+ u", "_qs + indexName + u", "_qs;
- m_body += conversion(m_state.accumulatorIn, elementType, use(m_state.accumulatorVariableIn))
+ m_body += conversion(m_state.accumulatorIn(), elementType, use(m_state.accumulatorVariableIn))
+ u");\n"_qs;
}
@@ -847,14 +905,14 @@ void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
void QQmlJSCodeGenerator::generateEnumLookup(int index)
{
- const QString enumMember = m_state.accumulatorOut.enumMember();
+ const QString enumMember = m_state.accumulatorOut().enumMember();
// If we're referring to the type, there's nothing to do.
if (enumMember.isEmpty())
return;
// If the metaenum has the value, just use it and skip all the rest.
- const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut.enumeration();
+ const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
if (metaEnum.hasValues()) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ QString::number(metaEnum.value(enumMember));
@@ -862,7 +920,7 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
return;
}
- const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut.scopeType();
+ const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
// Otherwise we would have found an enum with values.
Q_ASSERT(!scopeType->isComposite());
@@ -880,12 +938,13 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
void QQmlJSCodeGenerator::generateTypeLookup(int index)
{
const QString indexString = QString::number(index);
+ const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator);
const QString namespaceString
- = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ = accumulatorIn.isImportNamespace()
+ ? QString::number(accumulatorIn.importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- switch (m_state.accumulatorOut.variant()) {
+ switch (m_state.accumulatorOut().variant()) {
case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
@@ -895,7 +954,6 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
break;
}
case QQmlJSRegisterContent::ScopeModulePrefix:
- m_body += m_state.accumulatorVariableOut + u" = aotContext->qmlScopeObject;\n"_qs;
break;
case QQmlJSRegisterContent::ScopeAttached: {
const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString
@@ -921,33 +979,75 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
}
}
+void QQmlJSCodeGenerator::generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType)
+{
+ if (changedRegisterVariable().isEmpty())
+ return;
+
+ const QQmlJSRegisterContent changed = m_state.changedRegister();
+ if (!m_typeResolver->equals(changed.storedType(), m_typeResolver->varType()))
+ return;
+
+ const QQmlJSScope::ConstPtr target = m_typeResolver->containedType(changed);
+ if (m_typeResolver->equals(target, containedType)
+ || m_typeResolver->equals(target, m_typeResolver->varType())) {
+ return;
+ }
+
+ // If we could store the type directly, we would not wrap it in a QVariant.
+ // Therefore, our best bet here is metaTypeFromName().
+ m_body += changedRegisterVariable() + u".convert("_qs + metaTypeFromName(target) + u");\n"_qs;
+}
+
void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
- if (m_state.accumulatorOut.isMethod()) {
+ if (m_state.accumulatorOut().isMethod()) {
reject(u"lookup of function property."_qs);
return;
}
- if (m_state.accumulatorOut.isEnumeration()) {
- generateEnumLookup(index);
+ if (m_state.accumulatorOut().isImportNamespace()) {
+ Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix);
+ // If we have an object module prefix, we need to pass through the original object.
+ if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs
+ + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
+ use(m_state.accumulatorVariableIn))
+ + u";\n"_qs;
+ } else {
+ m_body.setWriteRegister(QString());
+ }
return;
}
- if (m_state.accumulatorOut.isImportNamespace()) {
- m_body.setWriteRegister(QString());
- return; // Nothing to do. We've resolved the prefix already.
+ AccumulatorConverter registers(this);
+
+ if (m_state.accumulatorOut().isEnumeration()) {
+ generateEnumLookup(index);
+ return;
}
const QString indexString = QString::number(index);
- const QString namespaceString = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
+ ? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- const auto storedType = m_state.accumulatorIn.storedType();
- const bool isReferenceType
- = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectAttached) {
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const bool isReferenceType = (accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Reference);
+
+ switch (m_state.accumulatorOut().variant()) {
+ case QQmlJSRegisterContent::ScopeAttached: {
+ const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString
+ + u", aotContext->qmlScopeObject, &"_qs + m_state.accumulatorVariableOut + u')';
+ const QString initialization = u"aotContext->initLoadAttachedLookup("_qs
+ + indexString + u", "_qs + namespaceString
+ + u", aotContext->qmlScopeObject)"_qs;
+ generateLookup(lookup, initialization);
+ return;
+ }
+ case QQmlJSRegisterContent::ObjectAttached: {
if (!isReferenceType) {
// This can happen on incomplete type information. We contextually know that the
// type must be a QObject, but we cannot construct the inheritance chain. Then we
@@ -964,7 +1064,8 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
+ use(m_state.accumulatorVariableIn) + u')';
generateLookup(lookup, initialization);
return;
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton) {
+ }
+ case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
const QString initialization = u"aotContext->initLoadSingletonLookup("_qs
@@ -972,51 +1073,50 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
generateLookup(lookup, initialization);
return;
}
+ default:
+ break;
+ }
- Q_ASSERT(m_state.accumulatorOut.isProperty());
+ Q_ASSERT(m_state.accumulatorOut().isProperty());
- const QQmlJSScope::ConstPtr out = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr out = m_state.accumulatorOut().storedType();
if (isReferenceType) {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getObjectLookup("_qs + indexString
+ u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initGetObjectLookup("_qs
+ indexString + u", "_qs + use(m_state.accumulatorVariableIn)
- + u", "_qs + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if ((storedType == m_typeResolver->stringType()
- || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType())
+ || accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Sequence)
&& m_jsUnitGenerator->lookupName(index) == u"length"_qs) {
// Special-cased the same way as in QQmlJSTypeResolver::memberType()
m_body += m_state.accumulatorVariableOut + u" = "_qs + use(m_state.accumulatorVariableIn)
+ u".count("_qs;
- if (storedType == m_typeResolver->listPropertyType())
+ if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType()))
m_body += u'&' + use(m_state.accumulatorVariableIn);
m_body += u')' + u";\n"_qs;
- } else if (storedType == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
reject(u"lookup in QJSValue"_qs);
} else {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getValueLookup("_qs + indexString
- + u", "_qs + contentPointer(m_state.accumulatorIn,
+ + u", "_qs + contentPointer(m_state.accumulatorIn(),
use(m_state.accumulatorVariableIn))
- + u", "_qs + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + u", "_qs + contentPointer(m_state.accumulatorOut(),
+ m_state.accumulatorVariableOut)
+ u')';
const QString initialization = u"aotContext->initGetValueLookup("_qs
+ indexString + u", "_qs
- + metaObject(m_state.accumulatorOut.scopeType()) + u", "_qs
- + contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + metaObject(m_state.accumulatorOut().scopeType()) + u", "_qs
+ + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
}
}
@@ -1037,10 +1137,10 @@ void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
QString QQmlJSCodeGenerator::setLookupPreparation(
const QQmlJSRegisterContent &content, const QString &arg, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return u"const QMetaType argType = aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u");\n"_qs
+ u"if (argType.isValid())\n "_qs + arg + u".convert(argType)";
@@ -1058,9 +1158,8 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
m_body.setWriteRegister(QString());
const QString indexString = QString::number(index);
- const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
const QQmlJSRegisterContent callBase = registerType(baseReg);
- const QQmlJSScope::ConstPtr objectType = callBase.storedType();
const QQmlJSRegisterContent specific = m_typeResolver->memberType(
callBase, m_jsUnitGenerator->lookupName(index));
const QQmlJSRegisterContent property = specific.storedIn(
@@ -1072,9 +1171,10 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
QString variableInType;
QString preparation;
QString argType;
- if (property != m_state.accumulatorIn) {
+ if (!m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->containedType(property))) {
m_body += u"auto converted = "_qs
- + conversion(m_state.accumulatorIn, property, use(m_state.accumulatorVariableIn))
+ + conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
variableIn = contentPointer(property, u"converted"_qs);
variableInType = contentType(property, u"converted"_qs);
@@ -1089,7 +1189,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
argType = variableInType;
}
- switch (objectType->accessSemantics()) {
+ switch (callBase.storedType()->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference: {
const QString lookup = u"aotContext->setObjectLookup("_qs + indexString
+ u", "_qs + object + u", "_qs + variableIn + u')';
@@ -1105,7 +1205,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
- if (objectType != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) {
reject(u"SetLookup on sequence types (because of missing write-back)"_qs);
break;
}
@@ -1181,12 +1281,14 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
QString args;
QString conversions;
- if (m_typeResolver->containedType(m_state.accumulatorOut) == m_typeResolver->voidType()) {
+ if (m_state.changedRegisterIndex() == InvalidRegister ||
+ m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
types = u"QMetaType()"_qs;
args = u"nullptr"_qs;
} else {
*outVar = u"callResult"_qs;
- const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
m_body += outType->internalName();
if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
m_body += u" *"_qs;
@@ -1194,25 +1296,25 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
m_body += u' ';
m_body += *outVar + u";\n";
- types = metaTypeFromType(m_state.accumulatorOut.storedType());
+ types = metaTypeFromType(m_state.accumulatorOut().storedType());
args = u'&' + *outVar;
}
for (int i = 0; i < argc; ++i) {
- const QQmlJSScope::ConstPtr type = registerType(argv + i).storedType();
+ const QQmlJSRegisterContent content = registerType(argv + i);
const QString var = use(registerVariable(argv + i));
- if (type == m_typeResolver->jsPrimitiveType()) {
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
QString argName = u"arg"_qs + QString::number(i);
conversions += u"QVariant "_qs + argName + u" = "_qs
- + conversion(type, m_typeResolver->varType(), var) + u";\n"_qs;
+ + conversion(content.storedType(), m_typeResolver->varType(), var) + u";\n"_qs;
args += u", "_qs + argName + u".data()"_qs;
types += u", "_qs + argName + u".metaType()"_qs;
- } else if (type == m_typeResolver->varType()) {
+ } else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
args += u", "_qs + var + u".data()"_qs;
types += u", "_qs + var + u".metaType()"_qs;
} else {
args += u", &"_qs + var;
- types += u", "_qs + metaTypeFromType(type);
+ types += u", "_qs + metaTypeFromType(content.storedType());
}
}
return conversions
@@ -1222,17 +1324,13 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
{
- // Generate a new section to set m_state.accumulatorVariableOut,
- // so that m_state.accumulatorVariableOut can be optimized away.
+ if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
+ return;
nextSection();
m_body.setWriteRegister(m_state.accumulatorVariableOut);
m_body += m_state.accumulatorVariableOut + u" = "_qs;
-
- if (outVar.isEmpty())
- m_body += u"{};\n"_qs;
- else
- m_body += u"std::move(" + outVar + u");\n";
+ m_body += u"std::move(" + outVar + u");\n";
nextSection();
m_body.setHasSideEffects(true);
@@ -1272,8 +1370,10 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
addInclude(u"qalgorithms.h"_qs);
addInclude(u"qrandom.h"_qs);
- if (m_state.accumulatorOut.storedType() != m_typeResolver->realType())
- return false;
+ // If the result is not stored, we don't need to generate any code. All the math methods are
+ // conceptually pure functions.
+ if (m_state.changedRegisterIndex() != Accumulator)
+ return true;
m_body += u"{\n"_qs;
for (int i = 0; i < argc; ++i) {
@@ -1287,101 +1387,106 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ QString expression;
+
if (name == u"abs" && argc == 1) {
- m_body += u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
+ expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
} else if (name == u"acos"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
} else if (name == u"acosh"_qs && argc == 1) {
- m_body += u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
} else if (name == u"asin"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
} else if (name == u"asinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
} else if (name == u"atan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
} else if (name == u"atanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs;
} else if (name == u"atan2"_qs) {
// TODO: complicated
return false;
} else if (name == u"cbrt"_qs && argc == 1) {
- m_body += u"std::cbrt(arg1)"_qs;
+ expression = u"std::cbrt(arg1)"_qs;
} else if (name == u"ceil"_qs && argc == 1) {
- m_body += u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs;
+ expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs;
} else if (name == u"clz32"_qs && argc == 1) {
- m_body += u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
+ expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
} else if (name == u"cos"_qs && argc == 1) {
- m_body += u"std::cos(arg1)"_qs;
+ expression = u"std::cos(arg1)"_qs;
} else if (name == u"cosh"_qs && argc == 1) {
- m_body += u"std::cosh(arg1)"_qs;
+ expression = u"std::cosh(arg1)"_qs;
} else if (name == u"exp"_qs && argc == 1) {
- m_body += u"std::isinf(arg1) "
+ expression = u"std::isinf(arg1) "
"? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
": std::exp(arg1)"_qs.arg(inf);
} else if (name == u"expm1"_qs) {
// TODO: complicated
return false;
} else if (name == u"floor"_qs && argc == 1) {
- m_body += u"std::floor(arg1)"_qs;
+ expression = u"std::floor(arg1)"_qs;
} else if (name == u"fround"_qs && argc == 1) {
- m_body += u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
+ expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
"? arg1 "
": double(float(arg1))"_qs;
} else if (name == u"hypot"_qs) {
// TODO: complicated
return false;
} else if (name == u"imul"_qs && argc == 2) {
- m_body += u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
+ expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
"* quint32(QJSNumberCoercion::toInteger(arg2)))"_qs;
} else if (name == u"log"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
} else if (name == u"log10"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
} else if (name == u"log1p"_qs && argc == 1) {
- m_body += u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
} else if (name == u"log2"_qs && argc == 1) {
- m_body += u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
} else if (name == u"max"_qs && argc == 2) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
+ expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
"? arg2 "
": ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs;
} else if (name == u"min"_qs && argc == 2) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
+ expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
"? arg2 "
": ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs;
} else if (name == u"pow"_qs) {
// TODO: complicated
return false;
} else if (name == u"random"_qs && argc == 0) {
- m_body += u"QRandomGenerator::global()->generateDouble()"_qs;
+ expression = u"QRandomGenerator::global()->generateDouble()"_qs;
} else if (name == u"round"_qs && argc == 1) {
- m_body += u"std::isfinite(arg1) "
+ expression = u"std::isfinite(arg1) "
"? ((arg1 < 0.5 && arg1 >= -0.5) "
"? std::copysign(0.0, arg1) "
": std::floor(arg1 + 0.5)) "
": arg1"_qs;
} else if (name == u"sign"_qs && argc == 1) {
- m_body += u"std::isnan(arg1) "
+ expression = u"std::isnan(arg1) "
"? %1 "
": (qIsNull(arg1) "
"? arg1 "
": (std::signbit(arg1) ? -1.0 : 1.0))"_qs.arg(qNaN);
} else if (name == u"sin"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
} else if (name == u"sinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
} else if (name == u"sqrt"_qs && argc == 1) {
- m_body += u"std::sqrt(arg1)"_qs;
+ expression = u"std::sqrt(arg1)"_qs;
} else if (name == u"tan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
} else if (name == u"tanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
} else if (name == u"trunc"_qs && argc == 1) {
- m_body += u"std::trunc(arg1)"_qs;
+ expression = u"std::trunc(arg1)"_qs;
} else {
return false;
}
+ m_body += conversion(
+ m_typeResolver->realType(), m_state.accumulatorOut().storedType(), expression);
+
m_body += u";\n"_qs;
m_body += u"}\n"_qs;
return true;
@@ -1391,11 +1496,16 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
{
INJECT_TRACE_INFO(generate_CallPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
const QQmlJSRegisterContent baseType = registerType(base);
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
const QString name = m_jsUnitGenerator->stringForIndex(
m_jsUnitGenerator->lookupNameIndex(index));
- if (m_typeResolver->containedType(baseType) == mathObject()) {
+ if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) {
if (inlineMathMethod(name, argc, argv))
return;
}
@@ -1458,6 +1568,11 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
{
INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
m_body.setHasSideEffects(true);
const QString indexString = QString::number(index);
@@ -1541,7 +1656,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
generateSetInstructionPointer();
m_body += u"aotContext->engine->throwError("_qs
- + conversion(m_state.accumulatorIn, m_typeResolver->globalType(
+ + conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
m_typeResolver->jsValueType()),
use(m_state.accumulatorVariableIn)) + u");\n"_qs;
m_body += u"return "_qs + errorReturnValue() + u";\n"_qs;
@@ -1674,9 +1789,15 @@ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- reject(u"DefineArray"_qs);
+ if (argc > 0)
+ reject(u"DefineArray"_qs);
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->emptyListType());
}
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1745,7 +1866,7 @@ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
m_body.setWriteRegister(QString());
m_body += u"if ("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1760,7 +1881,7 @@ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
m_body.setWriteRegister(QString());
m_body += u"if (!"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1799,42 +1920,47 @@ void QQmlJSCodeGenerator::generate_CmpEqNull()
{
INJECT_TRACE_INFO(generate_CmlEqNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
-
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeNull()
{
INJECT_TRACE_INFO(generate_CmlNeNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
{
- if (m_state.accumulatorIn.storedType() == m_typeResolver->intType())
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType()))
return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->boolType()) {
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) {
return QString::number(lhsConst) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->intType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
use(m_state.accumulatorVariableIn));
}
- if (m_typeResolver->isNumeric(m_state.accumulatorIn)) {
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
return conversion(m_typeResolver->intType(), m_typeResolver->realType(),
QString::number(lhsConst)) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->realType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(),
use(m_state.accumulatorVariableIn));
}
@@ -1842,7 +1968,7 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(),
QString::number(lhsConst));
result += u".equals("_qs;
- result += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
result += u')';
return result;
@@ -1851,10 +1977,10 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
QString QQmlJSCodeGenerator::getLookupPreparation(
const QQmlJSRegisterContent &content, const QString &var, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return var + u" = QVariant(aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u"))"_qs;
}
@@ -1865,9 +1991,9 @@ QString QQmlJSCodeGenerator::getLookupPreparation(
QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored)
+ if (m_typeResolver->registerContains(content, stored))
return u'&' + var;
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType()))
return var + u".data()"_qs;
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u'&' + var;
@@ -1881,9 +2007,9 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
const QQmlJSScope::ConstPtr stored = content.storedType();
const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
m_typeResolver->containedType(content));
- if (contained == stored)
+ if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->equals(stored, m_typeResolver->varType()))
return var + u".metaType()"_qs; // We expect the QVariant to be initialized
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return metaTypeFromName(contained);
@@ -1896,16 +2022,20 @@ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpEqInt);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + eqIntExpression(lhsConst)
- + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ eqIntExpression(lhsConst)) + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpNeInt);
- m_body += m_state.accumulatorVariableOut + u" = !("_qs + eqIntExpression(lhsConst)
- + u");\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!("_qs + eqIntExpression(lhsConst) + u')') + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
@@ -1973,17 +2103,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
INJECT_TRACE_INFO(generate_As);
const QString input = use(registerVariable(lhs));
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut);
+ const QQmlJSScope::ConstPtr contained
+ = m_typeResolver->containedType(m_state.readRegister(lhs));
m_body += m_state.accumulatorVariableOut + u" = "_qs;
- if (m_state.accumulatorIn.storedType() == m_typeResolver->metaObjectType()
+ if (m_typeResolver->equals(
+ m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())
&& contained->isComposite()) {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
use(m_state.accumulatorVariableIn) + u"->cast("_qs + input + u')');
} else {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
u'(' + metaObject(contained) + u")->cast("_qs + input + u')');
}
m_body += u";\n"_qs;
@@ -1992,31 +2124,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
void QQmlJSCodeGenerator::generate_UNot()
{
INJECT_TRACE_INFO(generate_UNot);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"!"_qs);
}
void QQmlJSCodeGenerator::generate_UPlus()
{
INJECT_TRACE_INFO(generate_UPlus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= +"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"+"_qs);
}
void QQmlJSCodeGenerator::generate_UMinus()
{
INJECT_TRACE_INFO(generate_UMinus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= -"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"-"_qs);
}
void QQmlJSCodeGenerator::generate_UCompl()
@@ -2027,25 +2147,13 @@ void QQmlJSCodeGenerator::generate_UCompl()
void QQmlJSCodeGenerator::generate_Increment()
{
INJECT_TRACE_INFO(generate_Increment);
- if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
- m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn)) + u"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"++"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"++"_qs);
}
void QQmlJSCodeGenerator::generate_Decrement()
{
INJECT_TRACE_INFO(generate_Decrement);
- if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
- m_body += m_state.accumulatorVariableOut + u" = "_qs
- + conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn)) + u"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"--"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"--"_qs);
}
void QQmlJSCodeGenerator::generate_Add(int lhs)
@@ -2152,16 +2260,18 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
use(registerVariable(lhs)));
const auto rhsVar = conversion(
- m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
+ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
u'(' + lhsVar + u" % "_qs + rhsVar + u')');
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->jsPrimitiveType());
}
void QQmlJSCodeGenerator::generate_Sub(int lhs)
@@ -2217,9 +2327,15 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
- m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
- .value(m_state.accumulatorIn.storedType());
- Q_ASSERT(!m_state.accumulatorIn.isValid() || !m_state.accumulatorVariableIn.isEmpty());
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end()
+ && isTypeStorable(m_typeResolver, accumulatorIn.value().storedType())) {
+ m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
+ .value(accumulatorIn.value().storedType());
+ Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
+ } else {
+ m_state.accumulatorVariableIn.clear();
+ }
auto labelIt = m_labels.constFind(currentInstructionOffset());
if (labelIt != m_labels.constEnd()) {
@@ -2234,38 +2350,38 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
nextSection();
- m_state.accumulatorVariableOut = registerVariable(QQmlJSTypePropagator::Accumulator);
+ if (m_state.changedRegisterIndex() == Accumulator)
+ m_state.accumulatorVariableOut = changedRegisterVariable();
+ else
+ m_state.accumulatorVariableOut.clear();
+
if (!m_state.accumulatorVariableOut.isEmpty())
m_body.setWriteRegister(m_state.accumulatorVariableOut);
// If the accumulator type is valid, we want an accumulator variable.
// If not, we don't want one.
- // If the stored type is void, we don't need a variable, but we want to transport the type
- // information for any enum access or similar.
- Q_ASSERT((m_state.accumulatorOut.isValid()
- && m_state.accumulatorOut.storedType() != m_typeResolver->voidType())
+ Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
|| m_state.accumulatorVariableOut.isEmpty());
- Q_ASSERT(!m_state.accumulatorOut.isValid()
- || m_state.accumulatorOut.storedType() == m_typeResolver->voidType()
- || !m_state.accumulatorVariableOut.isEmpty());
+ Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
+ || !m_state.accumulatorVariableOut.isEmpty()
+ || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
const int currentLine = currentSourceLocation().startLine;
if (currentLine != m_lastLineNumberUsed) {
const int nextLine = nextJSLine(currentLine);
- if (nextLine == currentLine + 1 || nextLine < 1) {
+ for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
m_body += u"// "_qs;
- m_body += m_sourceCodeLines.value(currentLine - 1).trimmed();
+ m_body += m_sourceCodeLines.value(line).trimmed();
m_body += u'\n';
- } else {
- m_body += u"/*\n"_qs;
- for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
- m_body += m_sourceCodeLines.at(line).trimmed();
- m_body += u'\n';
- }
- m_body += u"*/\n"_qs;
}
m_lastLineNumberUsed = currentLine;
}
+
+ // If the instruction has no side effects and doesn't write any register, it's dead.
+ // We might still need the label, though, and the source code comment.
+ if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty())
+ return SkipInstruction;
+
return ProcessInstruction;
}
@@ -2292,79 +2408,134 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
const QQmlJSRegisterContent lhsContent = registerType(lhs);
auto isComparable = [&]() {
if (m_typeResolver->isPrimitive(lhsContent)
- && m_typeResolver->isPrimitive(m_state.accumulatorIn)) {
+ && m_typeResolver->isPrimitive(m_state.accumulatorIn())) {
return true;
}
- if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn.isEnumeration())
+ if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration())
return true;
- if (m_typeResolver->isNumeric(m_state.accumulatorIn) && lhsContent.isEnumeration())
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration())
return true;
return false;
};
- if (!isComparable())
- reject(u"equality comparison on non-primitive types"_qs);
+ if (!isComparable()) {
+ reject(u"equality comparison on non-primitive types %1 and %2"_qs.arg(
+ m_state.accumulatorIn().descriptiveName(), lhsContent.descriptiveName()));
+ }
const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
- const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType();
m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto primitive = m_typeResolver->jsPrimitiveType();
- if (lhsType == rhsType && lhsType != primitive) {
- m_body += use(registerVariable(lhs));
- m_body += (invert ? u" != "_qs : u" == "_qs);
- m_body += use(m_state.accumulatorVariableIn);
+ if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ use(registerVariable(lhs)) + (invert ? u" != "_qs : u" == "_qs)
+ + use(m_state.accumulatorVariableIn));
} else {
- if (invert)
- m_body += u'!';
- m_body += conversion(registerType(lhs).storedType(), primitive, use(registerVariable(lhs)));
- m_body += u'.';
- m_body += function;
- m_body += u'(';
- m_body += conversion(m_state.accumulatorIn.storedType(), primitive,
- use(m_state.accumulatorVariableIn));
- m_body += u')';
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ (invert ? u"!"_qs : QString())
+ + conversion(registerType(lhs).storedType(), primitive,
+ use(registerVariable(lhs)))
+ + u'.' + function + u'(' + conversion(
+ m_state.accumulatorIn().storedType(), primitive,
+ use(m_state.accumulatorVariableIn))
+ + u')');
}
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
{
- m_body += m_state.accumulatorVariableOut;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto lhsType = registerType(lhs);
const QQmlJSScope::ConstPtr compareType =
- m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn)
- ? m_typeResolver->merge(lhsType, m_state.accumulatorIn).storedType()
+ m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
+ ? m_typeResolver->merge(lhsType, m_state.accumulatorIn()).storedType()
: m_typeResolver->jsPrimitiveType();
- m_body += u" = "_qs;
- m_body += conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)));
- m_body += u' ';
- m_body += cppOperator;
- m_body += u' ';
- m_body += conversion(m_state.accumulatorIn.storedType(), compareType,
- use(m_state.accumulatorVariableIn));
+
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)))
+ + u' ' + cppOperator + u' '
+ + conversion(m_state.accumulatorIn().storedType(), compareType,
+ use(m_state.accumulatorVariableIn)));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
{
- const auto lhsVar = conversion(registerType(lhs), m_state.accumulatorOut,
+ const auto lhsVar = conversion(registerType(lhs), m_state.readRegister(lhs),
use(registerVariable(lhs)));
- const auto rhsVar = conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
+ const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += lhsVar;
- m_body += u' ';
- m_body += cppOperator;
- m_body += u' ';
- m_body += rhsVar;
+ m_body += conversion(
+ originalOut, m_state.accumulatorOut(),
+ u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
+}
+
+void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
+{
+ const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
+ use(m_state.accumulatorVariableIn));
+
+ if (var == m_state.accumulatorVariableOut) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + cppOperator + var + u";\n"_qs;
+ return;
+ }
+
+ const auto original = m_typeResolver->original(m_state.accumulatorOut());
+ if (m_state.accumulatorOut() == original) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs
+ + cppOperator + m_state.accumulatorVariableOut + u";\n"_qs;
+ return;
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
+ m_typeResolver->original(m_state.accumulatorOut()),
+ m_state.accumulatorOut(), cppOperator + var) + u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
+}
+
+void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
+{
+ const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
+ use(m_state.accumulatorVariableIn));
+
+ if (var == m_state.accumulatorVariableOut) {
+ m_body += cppOperator + var + u";\n"_qs;
+ return;
+ }
+
+ const auto original = m_typeResolver->original(m_state.accumulatorOut());
+ if (m_state.accumulatorOut() == original) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs;
+ m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_qs;
+ return;
+ }
+
+ m_body += u"{\n"_qs;
+ m_body += u"auto converted = "_qs + var + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
+ m_typeResolver->original(m_state.accumulatorOut()),
+ m_state.accumulatorOut(), cppOperator + u"converted"_qs) + u";\n"_qs;
+ m_body += u"}\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
@@ -2381,19 +2552,6 @@ void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &i
m_body += u"}\n"_qs;
}
-void QQmlJSCodeGenerator::protectAccumulator()
-{
- // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
- // prepare the output QVariant, and afterwards use the input variant. Therefore we need to move
- // the input out of the way first.
- if (m_state.accumulatorVariableIn == m_state.accumulatorVariableOut
- && m_state.accumulatorOut.storedType() == m_typeResolver->varType()) {
- m_state.accumulatorVariableIn = use(m_state.accumulatorVariableIn) + u"_moved"_qs;
- m_body += u"QVariant "_qs + m_state.accumulatorVariableIn
- + u" = std::move("_qs + m_state.accumulatorVariableOut + u");\n"_qs;
- }
-}
-
void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset, JumpMode mode)
{
@@ -2402,32 +2560,47 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
m_body += u"{\n"_qs;
int absoluteOffset =nextInstructionOffset() + relativeOffset;
- const auto annotation = m_annotations->constFind(absoluteOffset);
+ const auto annotation = m_annotations->find(absoluteOffset);
if (annotation != m_annotations->constEnd()) {
- const auto &currentTypes = (*m_annotations)[currentInstructionOffset()].registers;
- const auto &conversions = annotation->expectedTargetTypesBeforeJump;
+ const auto &conversions = annotation->second.typeConversions;
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
regIt != regEnd; ++regIt) {
int registerIndex = regIt.key();
const QQmlJSRegisterContent targetType = regIt.value();
- if (!targetType.isValid() || !currentTypes.contains(registerIndex))
+ if (!targetType.isValid())
continue;
- const QQmlJSRegisterContent currentType = currentTypes.value(registerIndex);
- if (!currentType.isValid() || currentType == targetType)
+
+ QQmlJSRegisterContent currentType;
+ QString currentVariable;
+ if (registerIndex == m_state.changedRegisterIndex()) {
+ currentType = m_state.changedRegister();
+ currentVariable = changedRegisterVariable();
+ } else {
+ auto it = m_state.registers.find(registerIndex);
+ if (it == m_state.registers.end())
+ continue;
+ currentType = it.value();
+ currentVariable = registerVariable(registerIndex);
+ }
+
+ // Actually == here. We want the jump code also for equal types
+ if (currentType == targetType
+ || !isTypeStorable(m_typeResolver, targetType.storedType())) {
continue;
+ }
+
Q_ASSERT(m_registerVariables.contains(registerIndex));
const auto &currentRegisterVariables = m_registerVariables[registerIndex];
const auto variable = currentRegisterVariables.constFind(targetType.storedType());
- const QString oldVar = registerVariable(registerIndex);
- if (variable == currentRegisterVariables.end() || *variable == oldVar)
+ if (variable == currentRegisterVariables.end() || *variable == currentVariable)
continue;
nextSection();
m_body.setWriteRegister(*variable);
m_body += *variable;
m_body += u" = "_qs;
- m_body += conversion(currentTypes.value(registerIndex), targetType, use(oldVar));
+ m_body += conversion(currentType, targetType, use(currentVariable));
m_body += u";\n"_qs;
}
}
@@ -2449,117 +2622,210 @@ QString QQmlJSCodeGenerator::registerVariable(int index) const
if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) {
const int argumentIndex = index - QV4::CallData::OffsetCount;
return u"*static_cast<"_qs
- + castTargetName(m_function->argumentTypes[argumentIndex])
+ + castTargetName(m_function->argumentTypes[argumentIndex].storedType())
+ u"*>(argumentsPtr["_qs + QString::number(argumentIndex) + u"])"_qs;
}
return m_registerVariables.value(index).value(registerType(index).storedType());
}
+QString QQmlJSCodeGenerator::changedRegisterVariable() const
+{
+ return m_registerVariables.value(m_state.changedRegisterIndex()).value(
+ m_state.changedRegister().storedType());
+}
+
QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
{
- if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) {
- return m_typeResolver->globalType(
- m_function->argumentTypes[index - QV4::CallData::OffsetCount]);
- }
+ if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex())
+ return m_function->argumentTypes[index - QV4::CallData::OffsetCount];
return m_state.registers[index];
}
QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
- const QString &variable) const
+ const QString &variable)
{
// TODO: most values can be moved, which is much more efficient with the common types.
// add a move(from, to, variable) function that implements the moves.
Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
- if (from == to)
+ const auto jsValueType = m_typeResolver->jsValueType();
+ const auto varType = m_typeResolver->varType();
+ const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
+ const auto boolType = m_typeResolver->boolType();
+
+ auto zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) {
+ if (m_typeResolver->equals(to, boolType))
+ return u"false"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
+ return u"0"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->floatType()))
+ return u"0.0f"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
+ return u"0.0"_qs;
+ return QString();
+ };
+
+ if (m_typeResolver->equals(from, m_typeResolver->voidType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
+ const QString zero = zeroBoolOrNumeric(to);
+ if (!zero.isEmpty())
+ return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
+ return QQmlJSUtils::toLiteral(u"undefined"_qs);
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ // Anything else is just the default constructed type.
+ return to->augmentedInternalName() + u"()"_qs;
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->nullType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
+ if (m_typeResolver->equals(to, jsValueType))
+ return u"QJSValue(QJSValue::NullValue)"_qs;
+ if (m_typeResolver->equals(to, jsPrimitiveType))
+ return u"QJSPrimitiveValue(QJSPrimitiveNull())"_qs;
+ if (m_typeResolver->equals(to, varType))
+ return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
+ const QString zero = zeroBoolOrNumeric(to);
+ if (!zero.isEmpty())
+ return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
+ return QQmlJSUtils::toLiteral(u"null"_qs);
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ reject(u"Conversion from null to %1"_qs.arg(to->internalName()));
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->emptyListType())) {
+ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ return castTargetName(to) + u"()"_qs;
+ if (m_typeResolver->equals(to, m_typeResolver->varType()))
+ return u"QVariant(QVariantList())"_qs;
+ if (m_typeResolver->equals(from, to))
+ return QString();
+ reject(u"Conversion from empty list to %1"_qs.arg(to->internalName()));
+ }
+
+ if (m_typeResolver->equals(from, to))
return variable;
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
// We still have to cast as other execution paths may result in different types.
- if (base == to)
+ if (m_typeResolver->equals(base, to))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
- if (base == from)
+ if (m_typeResolver->equals(base, from))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
- } else if (to == m_typeResolver->boolType()) {
+ } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
return u'(' + variable + u" != nullptr)"_qs;
}
}
- if (from == m_typeResolver->nullType()
- && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
- }
-
- const auto jsValueType = m_typeResolver->jsValueType();
-
auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
- return candidate == jsValueType || candidate->isScript();
+ return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
};
if (isJsValue(from) && isJsValue(to))
return variable;
- const auto boolType = m_typeResolver->boolType();
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
- || type == m_typeResolver->boolType()
+ || m_typeResolver->equals(type, m_typeResolver->boolType())
|| type->scopeType() == QQmlJSScope::EnumScope;
};
- if (from == m_typeResolver->realType() && to == m_typeResolver->intType())
+ if (m_typeResolver->equals(from, m_typeResolver->realType())
+ && m_typeResolver->equals(to, m_typeResolver->intType())) {
return u"QJSNumberCoercion::toInteger("_qs + variable + u')';
+ }
if (isBoolOrNumber(from) && isBoolOrNumber(to))
return to->internalName() + u'(' + variable + u')';
- const auto varType = m_typeResolver->varType();
- const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
- if (from == jsPrimitiveType) {
- if (to == m_typeResolver->realType())
+
+ if (m_typeResolver->equals(from, jsPrimitiveType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
return variable + u".toDouble()"_qs;
- if (to == boolType)
+ if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_qs;
- if (to == m_typeResolver->intType())
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
return variable + u".toInteger()"_qs;
- if (to == m_typeResolver->stringType())
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return variable + u".toString()"_qs;
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"QJSValue(QJSPrimitiveValue("_qs + variable + u"))"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant()"_qs;
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
}
if (isJsValue(from)) {
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return variable + u".toPrimitive()"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant(QJSValue::RetainJSObjects)"_qs;
return u"qjsvalue_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
}
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return u"QJSPrimitiveValue("_qs + variable + u')';
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"aotContext->engine->toScriptValue("_qs + variable + u')';
- if (from == varType) {
- if (to == m_typeResolver->listPropertyType())
+ if (m_typeResolver->equals(from, varType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->listPropertyType()))
return u"QQmlListReference("_qs + variable + u", aotContext->qmlEngine())"_qs;
- return u"qvariant_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
+ return u"aotContext->engine->fromVariant<"_qs + castTargetName(to) + u">("_qs
+ + variable + u')';
}
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return u"QVariant::fromValue("_qs + variable + u')';
+ if (m_typeResolver->equals(from, m_typeResolver->urlType())
+ && m_typeResolver->equals(to, m_typeResolver->stringType())) {
+ return variable + u".toString()"_qs;
+ }
+
+ if (m_typeResolver->equals(from, m_typeResolver->stringType())
+ && m_typeResolver->equals(to, m_typeResolver->urlType())) {
+ return u"QUrl("_qs + variable + u')';
+ }
+
+ const auto retrieveFromPrimitive = [&](const QQmlJSScope::ConstPtr &type)
+ {
+ if (m_typeResolver->equals(type, m_typeResolver->boolType()))
+ return u".toBool()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->intType()))
+ return u".toInteger()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->realType()))
+ return u".toDouble()"_qs;
+ if (m_typeResolver->equals(type, m_typeResolver->stringType()))
+ return u".toString()"_qs;
+ return QString();
+ };
+
+ const auto fitsIntoPrimitive = [&](const QQmlJSScope::ConstPtr &type)
+ {
+ return !retrieveFromPrimitive(type).isEmpty()
+ || m_typeResolver->equals(type, m_typeResolver->floatType());
+ };
+
+ if (fitsIntoPrimitive(from)) {
+ const QString retrieve = retrieveFromPrimitive(to);
+ if (!retrieve.isEmpty())
+ return u"QJSPrimitiveValue("_qs + variable + u')' + retrieve;
+ }
+
// TODO: more efficient string conversions, possibly others
return u"aotContext->engine->fromScriptValue<"_qs + castTargetName(to)
@@ -2586,4 +2852,58 @@ void QQmlJSCodeGenerator::reject(const QString &thing)
setError(u"Cannot generate efficient code for %1"_qs.arg(thing));
}
+QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
+ : accumulatorOut(generator->m_state.accumulatorOut())
+ , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
+ , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
+ , generator(generator)
+{
+ if (accumulatorVariableOut.isEmpty())
+ return;
+
+ const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
+ const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
+ const QQmlJSScope::ConstPtr origStored = resolver->originalType(accumulatorOut.storedType());
+
+ if (!resolver->equals(origContained, resolver->containedType(accumulatorOut))
+ || !resolver->equals(origStored, accumulatorOut.storedType())) {
+ generator->m_state.accumulatorVariableOut = u"retrieved"_qs;
+ generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut));
+ generator->m_body += u"{\n"_qs;
+ generator->m_body += origStored->augmentedInternalName() + u' '
+ + generator->m_state.accumulatorVariableOut + u";\n";
+ } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
+ && generator->m_state.readsRegister(Accumulator)
+ && resolver->registerIsStoredIn(
+ generator->m_state.accumulatorOut(), resolver->varType())) {
+ // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
+ // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
+ // move the input out of the way first.
+ generator->m_state.accumulatorVariableIn = generator->use(
+ generator->m_state.accumulatorVariableIn) + u"_moved"_qs;
+ generator->m_body += u"{\n"_qs;
+ generator->m_body += u"QVariant "_qs + generator->m_state.accumulatorVariableIn
+ + u" = std::move("_qs + generator->m_state.accumulatorVariableOut + u");\n"_qs;
+ }
+}
+
+QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
+{
+ if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
+ generator->m_body += accumulatorVariableOut + u" = "_qs + generator->conversion(
+ generator->m_state.accumulatorOut(), accumulatorOut,
+ generator->m_state.accumulatorVariableOut) + u";\n"_qs;
+ const auto contained = generator->m_typeResolver->containedType(
+ generator->m_state.accumulatorOut());
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.setRegister(Accumulator, accumulatorOut);
+ generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
+ generator->generateOutputVariantConversion(contained);
+ } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
+ }
+}
+
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 7ef865f551..5128b585d7 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -117,6 +117,22 @@ protected:
QString accumulatorVariableOut;
};
+ // This is an RAII helper we can use to automatically convert the result of "inflexible"
+ // operations to the desired type. For example GetLookup can only retrieve the type of
+ // the property we're looking up. If we want to store a different type, we need to convert.
+ struct AccumulatorConverter
+ {
+ Q_DISABLE_COPY_MOVE(AccumulatorConverter);
+ AccumulatorConverter(QQmlJSCodeGenerator *generator);
+ ~AccumulatorConverter();
+
+ private:
+ const QQmlJSRegisterContent accumulatorOut;
+ const QString accumulatorVariableIn;
+ const QString accumulatorVariableOut;
+ QQmlJSCodeGenerator *generator = nullptr;
+ };
+
virtual QString metaObject(const QQmlJSScope::ConstPtr &objectType);
void generate_Ret() override;
@@ -272,16 +288,16 @@ protected:
QString conversion(const QQmlJSRegisterContent &from,
const QQmlJSRegisterContent &to,
- const QString &variable) const
+ const QString &variable)
{
return conversion(from.storedType(), to.storedType(), variable);
}
QString conversion(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
- const QString &variable) const;
+ const QString &variable);
- QString errorReturnValue() const;
+ QString errorReturnValue();
void reject(const QString &thing);
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const;
@@ -299,6 +315,7 @@ protected:
void generateEnumLookup(int index);
QString registerVariable(int index) const;
+ QString changedRegisterVariable() const;
QQmlJSRegisterContent registerType(int index) const;
Section m_body;
@@ -326,15 +343,16 @@ private:
void generateCompareOperation(int lhs, const QString &cppOperator);
void generateArithmeticOperation(int lhs, const QString &cppOperator);
void generateJumpCodeWithTypeConversions(int relativeOffset, JumpMode mode);
+ void generateUnaryOperation(const QString &cppOperator);
+ void generateInPlaceOperation(const QString &cppOperator);
void generateMoveOutVar(const QString &outVar);
void generateTypeLookup(int index);
+ void generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType);
QString eqIntExpression(int lhsConst);
QString argumentsList(int argc, int argv, QString *outVar);
QString castTargetName(const QQmlJSScope::ConstPtr &type) const;
- void protectAccumulator();
-
QList<BasicBlock> findBasicBlocks(const QList<Section> &sections);
RequiredRegisters dropPreserveCycles(
const QList<BasicBlock> &basicBlocks, const RequiredRegisters &requiredRegisters);
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index d7f9336ee9..af5c9204e6 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -46,6 +46,7 @@
#include <private/qqmljstyperesolver_p.h>
#include <private/qv4bytecodehandler_p.h>
#include <private/qv4compiler_p.h>
+#include <private/qflatmap_p.h>
QT_BEGIN_NAMESPACE
@@ -54,6 +55,7 @@ class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
public:
enum RegisterShortcuts {
+ InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
FirstArgument = QV4::CallData::OffsetCount
};
@@ -61,20 +63,28 @@ public:
using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable;
// map from register index to expected type
- using VirtualRegisters = QHash<int, QQmlJSRegisterContent>;
+ using VirtualRegisters = QFlatMap<int, QQmlJSRegisterContent>;
struct InstructionAnnotation
{
- VirtualRegisters registers;
- VirtualRegisters expectedTargetTypesBeforeJump;
+ // Registers explicit read as part of the instruction.
+ VirtualRegisters readRegisters;
+
+ // Registers that have to be converted for future instructions after a jump.
+ VirtualRegisters typeConversions;
+
+ QQmlJSRegisterContent changedRegister;
+ int changedRegisterIndex = InvalidRegister;
+ bool hasSideEffects = false;
+ bool isRename = false;
};
- using InstructionAnnotations = QHash<int, InstructionAnnotation>;
+ using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
struct Function
{
QQmlJSScopesById addressableScopes;
- QList<QQmlJSScope::ConstPtr> argumentTypes;
+ QList<QQmlJSRegisterContent> argumentTypes;
QQmlJSScope::ConstPtr returnType;
QQmlJSScope::ConstPtr qmlScope;
QByteArray code;
@@ -86,8 +96,80 @@ public:
struct State
{
VirtualRegisters registers;
- QQmlJSRegisterContent accumulatorIn;
- QQmlJSRegisterContent accumulatorOut;
+
+ const QQmlJSRegisterContent &accumulatorIn() const
+ {
+ auto it = registers.find(Accumulator);
+ Q_ASSERT(it != registers.end());
+ return it.value();
+ };
+
+ const QQmlJSRegisterContent &accumulatorOut() const
+ {
+ Q_ASSERT(m_changedRegisterIndex == Accumulator);
+ return m_changedRegister;
+ };
+
+ void setRegister(int registerIndex, QQmlJSRegisterContent content)
+ {
+ m_changedRegister = std::move(content);
+ m_changedRegisterIndex = registerIndex;
+ }
+
+ void clearChangedRegister()
+ {
+ m_changedRegisterIndex = InvalidRegister;
+ m_changedRegister = QQmlJSRegisterContent();
+ }
+
+ int changedRegisterIndex() const { return m_changedRegisterIndex; }
+ const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; }
+
+ void addReadRegister(int registerIndex, const QQmlJSRegisterContent &reg)
+ {
+ Q_ASSERT(reg.isConversion());
+ m_readRegisters[registerIndex] = reg;
+ }
+
+ void addReadAccumulator(const QQmlJSRegisterContent &reg)
+ {
+ addReadRegister(Accumulator, reg);
+ }
+
+ VirtualRegisters takeReadRegisters() const { return std::move(m_readRegisters); }
+ void setReadRegisters(VirtualRegisters readReagisters)
+ {
+ m_readRegisters = std::move(readReagisters);
+ }
+
+ QQmlJSRegisterContent readRegister(int registerIndex) const
+ {
+ Q_ASSERT(m_readRegisters.contains(registerIndex));
+ return m_readRegisters[registerIndex];
+ }
+
+ QQmlJSRegisterContent readAccumulator() const
+ {
+ return readRegister(Accumulator);
+ }
+
+ bool readsRegister(int registerIndex) const
+ {
+ return m_readRegisters.contains(registerIndex);
+ }
+
+ bool hasSideEffects() const { return m_hasSideEffects; }
+ void setHasSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; }
+
+ bool isRename() const { return m_isRename; }
+ void setIsRename(bool isRename) { m_isRename = isRename; }
+
+ private:
+ VirtualRegisters m_readRegisters;
+ QQmlJSRegisterContent m_changedRegister;
+ int m_changedRegisterIndex = InvalidRegister;
+ bool m_hasSideEffects = false;
+ bool m_isRename = false;
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
@@ -105,12 +187,12 @@ protected:
const Function *m_function = nullptr;
QQmlJS::DiagnosticMessage *m_error = nullptr;
- State initialState(const Function *function, const QQmlJSTypeResolver *resolver)
+ State initialState(const Function *function)
{
State state;
for (int i = 0; i < function->argumentTypes.count(); ++i) {
- state.registers[FirstArgument + i]
- = resolver->globalType(function->argumentTypes.at(i));
+ state.registers[FirstArgument + i] = function->argumentTypes.at(i);
+ Q_ASSERT(state.registers[FirstArgument + i].isValid());
}
return state;
}
@@ -120,23 +202,29 @@ protected:
{
State newState;
+ const auto instruction = annotations.find(currentInstructionOffset());
+ newState.registers = oldState.registers;
+
// Usually the initial accumulator type is the output of the previous instruction, but ...
- newState.accumulatorIn = oldState.accumulatorOut;
-
- const auto instruction = annotations.constFind(currentInstructionOffset());
- if (instruction != annotations.constEnd()) {
- const auto target = instruction->expectedTargetTypesBeforeJump.constFind(Accumulator);
- if (target != instruction->expectedTargetTypesBeforeJump.constEnd()) {
- // ... the initial type of the accumulator is given in expectedTargetTypesBeforeJump
- // if the current instruction can be jumped to.
- newState.accumulatorIn = *target;
- }
-
- newState.registers = instruction->registers;
- newState.accumulatorOut = instruction->registers[Accumulator];
- } else {
- newState.registers = VirtualRegisters();
- newState.accumulatorOut = QQmlJSRegisterContent();
+ if (oldState.changedRegisterIndex() != InvalidRegister)
+ newState.registers[oldState.changedRegisterIndex()] = oldState.changedRegister();
+
+ if (instruction == annotations.constEnd())
+ return newState;
+
+ newState.setHasSideEffects(instruction->second.hasSideEffects);
+ newState.setReadRegisters(instruction->second.readRegisters);
+ newState.setIsRename(instruction->second.isRename);
+
+ for (auto it = instruction->second.typeConversions.begin(),
+ end = instruction->second.typeConversions.end(); it != end; ++it) {
+ Q_ASSERT(it.key() != InvalidRegister);
+ newState.registers[it.key()] = it.value();
+ }
+
+ if (instruction->second.changedRegisterIndex != InvalidRegister) {
+ newState.setRegister(instruction->second.changedRegisterIndex,
+ instruction->second.changedRegister);
}
return newState;
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index f9448f38ac..c765a1c94e 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -29,6 +29,7 @@
#include "qqmljscompiler_p.h"
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmljsbasicblocks_p.h>
#include <private/qqmljscodegenerator_p.h>
#include <private/qqmljsfunctioninitializer_p.h>
#include <private/qqmljsimportvisitor_p.h>
@@ -669,26 +670,17 @@ QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose(
const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const
{
if (isStrict(m_document)
- && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
- && m_logger->isCategoryError(Log_Compiler)) {
+ && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
+ && !m_logger->isCategoryIgnored(Log_Compiler)
+ && m_logger->categoryLevel(Log_Compiler) == QtCriticalMsg) {
qFatal("%s:%d: (strict mode) %s",
qPrintable(QFileInfo(m_resourcePath).fileName()),
location.startLine, qPrintable(message));
}
- switch (type) {
- case QtDebugMsg:
- case QtInfoMsg:
- m_logger->logInfo(message, Log_Compiler, location);
- break;
- case QtWarningMsg:
- m_logger->logWarning(message, Log_Compiler, location);
- break;
- case QtCriticalMsg:
- case QtFatalMsg:
- m_logger->logCritical(message, Log_Compiler, location);
- break;
- }
+ // TODO: this is a special place that explicitly sets the severity through
+ // logger's private function
+ m_logger->log(message, Log_Compiler, location, type);
return QQmlJS::DiagnosticMessage {
message,
@@ -777,6 +769,9 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
if (error->isValid())
return compileError();
+ QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
+ typePropagationResult = basicBlocks.run(function, typePropagationResult);
+
QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
shadowCheck.run(&typePropagationResult, function, error);
if (error->isValid())
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 8cd3bb3536..c993197399 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -92,14 +92,20 @@ void QQmlJSFunctionInitializer::populateSignature(
for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) {
if (argument.typeAnnotation) {
if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) {
- function->argumentTypes.append(type);
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(type)));
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(m_typeResolver->varType())));
signatureError(u"Cannot resolve the argument type %1."_qs
.arg(argument.typeAnnotation->type->toString()));
}
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(
+ m_typeResolver->tracked(
+ m_typeResolver->globalType(m_typeResolver->varType())));
signatureError(u"Functions without type annotations won't be compiled"_qs);
}
}
@@ -181,7 +187,11 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
}
const auto property = m_objectType->property(propertyName);
- function.returnType = property.type();
+ function.returnType = property.isList()
+ ? m_typeResolver->listType(property.type())
+ : QQmlJSScope::ConstPtr(property.type());
+
+
if (!function.returnType) {
diagnose(u"Cannot resolve property type %1 for binding on %2"_qs.arg(
property.typeName(), propertyName),
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 4d9bf6ef29..e2cac15684 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -271,11 +271,29 @@ void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import,
for (auto const &dependency : qAsConst(import.dependencies))
importHelper(dependency.module, types, QString(), dependency.version, true);
+ bool hasOptionalImports = false;
for (auto const &import : qAsConst(import.imports)) {
+ if (import.flags & QQmlDirParser::Import::Optional) {
+ hasOptionalImports = true;
+ if (!m_useOptionalImports) {
+ continue;
+ }
+
+ if (!(import.flags & QQmlDirParser::Import::OptionalDefault))
+ continue;
+ }
+
importHelper(import.module, types, isDependency ? QString() : prefix,
(import.flags & QQmlDirParser::Import::Auto) ? version : import.version,
isDependency);
}
+
+ if (hasOptionalImports && !m_useOptionalImports) {
+ m_warnings.append(
+ { u"%1 uses optional imports which are not supported. Some types might not be found."_qs
+ .arg(import.name),
+ QtCriticalMsg, QQmlJS::SourceLocation() });
+ }
}
static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h
index 6198717bed..cacc8eeb9b 100644
--- a/src/qmlcompiler/qqmljsimporter_p.h
+++ b/src/qmlcompiler/qqmljsimporter_p.h
@@ -50,10 +50,12 @@ class QQmlJSImporter
public:
using ImportedTypes = QHash<QString, QQmlJSImportedScope>;
- QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper)
- : m_importPaths(importPaths)
- , m_builtins({})
- , m_mapper(mapper)
+ QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
+ bool useOptionalImports = false)
+ : m_importPaths(importPaths),
+ m_builtins({}),
+ m_mapper(mapper),
+ m_useOptionalImports(useOptionalImports)
{}
QQmlJSResourceFileMapper *resourceFileMapper() { return m_mapper; }
@@ -153,6 +155,7 @@ private:
QList<QQmlJS::DiagnosticMessage> m_warnings;
AvailableTypes m_builtins;
QQmlJSResourceFileMapper *m_mapper = nullptr;
+ bool m_useOptionalImports;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 5e4c2e5b9a..c482b6597c 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -124,6 +124,7 @@ void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QS
m_currentScope = QQmlJSScope::create(type, m_currentScope);
setScopeName(m_currentScope, type, name);
m_currentScope->setIsComposite(true);
+ m_currentScope->setFilePath(QFileInfo(m_logger->fileName()).absoluteFilePath());
m_currentScope->setSourceLocation(location);
m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
}
@@ -220,13 +221,13 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
if (doRequeue)
continue;
if (foundProperty) {
- m_logger->logWarning(QStringLiteral("Cannot deduce type of alias \"%1\"")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot deduce type of alias \"%1\"")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
} else {
- m_logger->logWarning(QStringLiteral("Cannot resolve alias \"%1\"")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
}
} else {
property.setType(type);
@@ -279,9 +280,9 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
for (const auto &property : properties) {
if (!property.isAlias() || property.type())
continue;
- m_logger->logWarning(QStringLiteral("Alias \"%1\" is part of an alias cycle")
- .arg(property.propertyName()),
- Log_Alias, object->sourceLocation());
+ m_logger->log(QStringLiteral("Alias \"%1\" is part of an alias cycle")
+ .arg(property.propertyName()),
+ Log_Alias, object->sourceLocation());
}
}
}
@@ -315,9 +316,9 @@ void QQmlJSImportVisitor::processImportWarnings(
if (warnings.isEmpty())
return;
- m_logger->logWarning(QStringLiteral("Warnings occurred while importing %1:").arg(what),
- Log_Import, srcLocation);
- m_logger->processMessages(warnings, QtWarningMsg, Log_Import);
+ m_logger->log(QStringLiteral("Warnings occurred while importing %1:").arg(what), Log_Import,
+ srcLocation);
+ m_logger->processMessages(warnings, Log_Import);
}
void QQmlJSImportVisitor::importBaseModules()
@@ -411,11 +412,11 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
unusedImports.remove(import);
for (const auto &import : unusedImports) {
- m_logger->logInfo(QString::fromLatin1("Unused import at %1:%2:%3")
- .arg(m_logger->fileName())
- .arg(import.startLine)
- .arg(import.startColumn),
- Log_UnusedImport, import);
+ m_logger->log(QString::fromLatin1("Unused import at %1:%2:%3")
+ .arg(m_logger->fileName())
+ .arg(import.startLine)
+ .arg(import.startColumn),
+ Log_UnusedImport, import);
}
}
@@ -519,9 +520,8 @@ void QQmlJSImportVisitor::processDefaultProperties()
}
if (!isComponent) {
- m_logger->logWarning(
- QStringLiteral("Cannot assign to non-existent default property"),
- Log_Property, it.value().constFirst()->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot assign to non-existent default property"),
+ Log_Property, it.value().constFirst()->sourceLocation());
}
continue;
@@ -530,7 +530,7 @@ void QQmlJSImportVisitor::processDefaultProperties()
const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
if (it.value().length() > 1 && !defaultProp.isList()) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("Cannot assign multiple objects to a default non-list property"),
Log_Property, it.value().constFirst()->sourceLocation());
}
@@ -554,9 +554,8 @@ void QQmlJSImportVisitor::processDefaultProperties()
continue;
}
- m_logger->logWarning(
- QStringLiteral("Cannot assign to default property of incompatible type"),
- Log_Property, scope->sourceLocation());
+ m_logger->log(QStringLiteral("Cannot assign to default property of incompatible type"),
+ Log_Property, scope->sourceLocation());
}
}
}
@@ -572,10 +571,9 @@ void QQmlJSImportVisitor::processPropertyTypes()
property.setType(propertyType);
type.scope->addOwnProperty(property);
} else {
- m_logger->logWarning(
- property.typeName()
- + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import, type.location);
+ m_logger->log(property.typeName()
+ + QStringLiteral(" was not found. Did you add all import paths?"),
+ Log_Import, type.location);
}
}
}
@@ -634,31 +632,28 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
if (objectBinding.onToken) {
if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueInterceptor"))) {
if (foundInterceptors.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Duplicate interceptor on property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Duplicate interceptor on property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else {
foundInterceptors.insert(uniqueBindingId);
}
} else if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueSource"))) {
if (foundValueSources.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Duplicate value source on property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Duplicate value source on property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else if (foundObjects.contains(uniqueBindingId)
|| foundLiterals.contains(uniqueBindingId)) {
- m_logger->logWarning(
- QStringLiteral("Cannot combine value source and binding on "
- "property \"%1\"")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Cannot combine value source and binding on "
+ "property \"%1\"")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else {
foundValueSources.insert(uniqueBindingId);
}
} else {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("On-binding for property \"%1\" has wrong type \"%2\"")
.arg(propertyName)
.arg(typeName),
@@ -667,7 +662,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
} else {
// TODO: Warn here if binding.hasValue() is true
if (foundValueSources.contains(uniqueBindingId)) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
.arg(propertyName),
@@ -680,28 +675,27 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
// If the current scope is not fully resolved we cannot tell whether the property exists
// or not (we already warn elsewhere)
} else if (!property.isValid()) {
- m_logger->logWarning(QStringLiteral("Property \"%1\" is invalid or does not exist")
- .arg(propertyName),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" is invalid or does not exist")
+ .arg(propertyName),
+ Log_Property, objectBinding.location);
} else if (property.type().isNull() || !property.type()->isFullyResolved()) {
// Property type is not fully resolved we cannot tell any more than this
- m_logger->logWarning(
- QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
- "missing an import.")
- .arg(propertyName)
- .arg(property.typeName()),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
+ "missing an import.")
+ .arg(propertyName)
+ .arg(property.typeName()),
+ Log_Property, objectBinding.location);
} else if (!childScope->isFullyResolved()) {
// If the childScope type is not fully resolved we cannot tell whether the type is
// incompatible (we already warn elsewhere)
} else {
// the type is incompatible
- m_logger->logWarning(QStringLiteral("Property \"%1\" of type \"%2\" is assigned an "
- "incompatible type \"%3\"")
- .arg(propertyName)
- .arg(property.typeName())
- .arg(getScopeName(childScope, QQmlJSScope::QMLScope)),
- Log_Property, objectBinding.location);
+ m_logger->log(QStringLiteral("Property \"%1\" of type \"%2\" is assigned an "
+ "incompatible type \"%3\"")
+ .arg(propertyName)
+ .arg(property.typeName())
+ .arg(getScopeName(childScope, QQmlJSScope::QMLScope)),
+ Log_Property, objectBinding.location);
}
}
}
@@ -710,7 +704,7 @@ void QQmlJSImportVisitor::checkRequiredProperties()
{
for (const auto &required : m_requiredProperties) {
if (!required.scope->hasProperty(required.name)) {
- m_logger->logWarning(
+ m_logger->log(
QStringLiteral("Property \"%1\" was marked as required but does not exist.")
.arg(required.name),
Log_Required, required.location);
@@ -764,24 +758,45 @@ void QQmlJSImportVisitor::checkRequiredProperties()
if (propertyUsedInRootAlias)
continue;
- const QString propertyScopeName = scopesToSearch.length() > 1
- ? getScopeName(scopesToSearch.at(scopesToSearch.length() - 2),
- QQmlJSScope::QMLScope)
+ const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.length() > 1
+ ? scopesToSearch.at(scopesToSearch.length() - 2)
+ : QQmlJSScope::ConstPtr();
+
+ const QString propertyScopeName = !propertyScope.isNull()
+ ? getScopeName(propertyScope, QQmlJSScope::QMLScope)
: u"here"_qs;
+
const QString requiredScopeName = prevRequiredScope
? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
: u"here"_qs;
+ std::optional<FixSuggestion> suggestion;
+
QString message =
QStringLiteral(
"Component is missing required property %1 from %2")
.arg(propName)
.arg(propertyScopeName);
- if (requiredScope != scope)
- message += QStringLiteral(" (marked as required by %3)")
- .arg(requiredScopeName);
+ if (requiredScope != scope) {
+ if (!prevRequiredScope.isNull()) {
+ auto sourceScope = prevRequiredScope->baseType();
+ suggestion = FixSuggestion {
+ { { u"%1:%2:%3: Property marked as required in %4"_qs
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation(), QString(),
+ sourceScope->filePath() } }
+ };
+ } else {
+ message += QStringLiteral(" (marked as required by %1)")
+ .arg(requiredScopeName);
+ }
+ }
- m_logger->logWarning(message, Log_Required, defScope->sourceLocation());
+ m_logger->log(message, Log_Required, defScope->sourceLocation(), true,
+ true, suggestion);
}
}
prevRequiredScope = requiredScope;
@@ -816,22 +831,20 @@ void QQmlJSImportVisitor::processPropertyBindings()
}
}
- m_logger->logWarning(
- QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
- "exists in the current element.")
- .arg(name),
- Log_Type, location, true, true, fixSuggestion);
+ m_logger->log(QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
+ "exists in the current element.")
+ .arg(name),
+ Log_Type, location, true, true, fixSuggestion);
continue;
}
const auto property = scope->property(name);
if (!property.type()) {
- m_logger->logWarning(
- QStringLiteral("No type found for property \"%1\". This may be due "
- "to a missing import statement or incomplete "
- "qmltypes files.")
- .arg(name),
- Log_Type, location);
+ m_logger->log(QStringLiteral("No type found for property \"%1\". This may be due "
+ "to a missing import statement or incomplete "
+ "qmltypes files.")
+ .arg(name),
+ Log_Type, location);
}
const auto &annotations = property.annotations();
@@ -849,7 +862,7 @@ void QQmlJSImportVisitor::processPropertyBindings()
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, location);
+ m_logger->log(message, Log_Deprecation, location);
}
}
}
@@ -928,9 +941,9 @@ void QQmlJSImportVisitor::checkSignals()
.arg(pair.first, pair.second.join(u", ")) } } };
}
- m_logger->logWarning(QStringLiteral("no matching signal found for handler \"%1\"")
- .arg(pair.first),
- Log_UnqualifiedAccess, location, true, true, fix);
+ m_logger->log(QStringLiteral("no matching signal found for handler \"%1\"")
+ .arg(pair.first),
+ Log_UnqualifiedAccess, location, true, true, fix);
continue;
}
@@ -938,10 +951,10 @@ void QQmlJSImportVisitor::checkSignals()
const QStringList signalParameters = signalMethod->parameterNames();
if (pair.second.length() > signalParameters.length()) {
- m_logger->logWarning(QStringLiteral("Signal handler for \"%2\" has more formal"
- " parameters than the signal it handles.")
- .arg(pair.first),
- Log_Signal, location);
+ m_logger->log(QStringLiteral("Signal handler for \"%2\" has more formal"
+ " parameters than the signal it handles.")
+ .arg(pair.first),
+ Log_Signal, location);
continue;
}
@@ -951,13 +964,13 @@ void QQmlJSImportVisitor::checkSignals()
if (j == i || j < 0)
continue;
- m_logger->logWarning(QStringLiteral("Parameter %1 to signal handler for \"%2\""
- " is called \"%3\". The signal has a parameter"
- " of the same name in position %4.")
- .arg(i + 1)
- .arg(pair.first, handlerParameter)
- .arg(j + 1),
- Log_Signal, location);
+ m_logger->log(QStringLiteral("Parameter %1 to signal handler for \"%2\""
+ " is called \"%3\". The signal has a parameter"
+ " of the same name in position %4.")
+ .arg(i + 1)
+ .arg(pair.first, handlerParameter)
+ .arg(j + 1),
+ Log_Signal, location);
}
}
}
@@ -1014,10 +1027,10 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina
inheritenceCycle.append(seen->baseTypeName());
}
- m_logger->logWarning(QStringLiteral("%1 is part of an inheritance cycle: %2")
- .arg(scope->internalName())
- .arg(inheritenceCycle),
- Log_InheritanceCycle);
+ m_logger->log(QStringLiteral("%1 is part of an inheritance cycle: %2")
+ .arg(scope->internalName())
+ .arg(inheritenceCycle),
+ Log_InheritanceCycle, scope->sourceLocation());
originalScope->clearBaseType();
break;
}
@@ -1026,12 +1039,11 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina
const auto newScope = scope->baseType();
if (newScope.isNull() && !scope->baseTypeName().isEmpty()) {
- m_logger->logWarning(
- scope->baseTypeName()
- + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import, scope->sourceLocation(), true, true,
- QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(),
- scope->sourceLocation()));
+ m_logger->log(scope->baseTypeName()
+ + QStringLiteral(" was not found. Did you add all import paths?"),
+ Log_Import, scope->sourceLocation(), true, true,
+ QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(),
+ scope->sourceLocation()));
}
scope = newScope;
@@ -1051,7 +1063,7 @@ void QQmlJSImportVisitor::checkDeprecation(const QQmlJSScope::ConstPtr &original
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, originalScope->sourceLocation());
+ m_logger->log(message, Log_Deprecation, originalScope->sourceLocation());
}
}
}
@@ -1072,12 +1084,12 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc
case QQmlJSScope::GroupedPropertyScope:
case QQmlJSScope::AttachedPropertyScope:
if (!childScope->baseType()) {
- m_logger->logWarning(QStringLiteral("unknown %1 property scope %2.")
- .arg(type == QQmlJSScope::GroupedPropertyScope
- ? QStringLiteral("grouped")
- : QStringLiteral("attached"),
- childScope->internalName()),
- Log_UnqualifiedAccess, childScope->sourceLocation());
+ m_logger->log(QStringLiteral("unknown %1 property scope %2.")
+ .arg(type == QQmlJSScope::GroupedPropertyScope
+ ? QStringLiteral("grouped")
+ : QStringLiteral("attached"),
+ childScope->internalName()),
+ Log_UnqualifiedAccess, childScope->sourceLocation());
}
children.append(childScope->childScopes());
default:
@@ -1121,10 +1133,36 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl)
if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
|| s.contains(QChar(0x2029u))) {
- m_logger->logWarning(QStringLiteral("String contains unescaped line terminator which is "
- "deprecated. Use a template "
- "literal instead."),
- Log_MultilineString, sl->literalToken);
+ QString templateString;
+
+ bool escaped = false;
+ const QChar stringQuote = s[0];
+ for (qsizetype i = 1; i < s.length() - 1; i++) {
+ const QChar c = s[i];
+
+ if (c == u'\\') {
+ escaped = !escaped;
+ } else if (escaped) {
+ // If we encounter an escaped quote, unescape it since we use backticks here
+ if (c == stringQuote)
+ templateString.chop(1);
+
+ escaped = false;
+ } else {
+ if (c == u'`')
+ templateString += u'\\';
+ if (c == u'$' && i + 1 < s.length() - 1 && s[i + 1] == u'{')
+ templateString += u'\\';
+ }
+
+ templateString += c;
+ }
+
+ const FixSuggestion suggestion = { { { u"Use a template literal instead"_qs,
+ sl->literalToken, u"`" + templateString + u"`" } } };
+ m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
+ "deprecated."),
+ Log_MultilineString, sl->literalToken, true, true, suggestion);
}
return true;
@@ -1181,8 +1219,8 @@ void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
{
if (!m_inlineComponentName.isNull()) {
- m_logger->logWarning(u"Nested inline components are not supported"_qs, Log_Syntax,
- component->firstSourceLocation());
+ m_logger->log(u"Nested inline components are not supported"_qs, Log_Syntax,
+ component->firstSourceLocation());
return true;
}
@@ -1234,9 +1272,9 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (const auto idExpression = cast<IdentifierExpression *>(node)) {
aliasExpr.prepend(idExpression->name.toString());
} else {
- m_logger->logWarning(QStringLiteral("Invalid alias expression. Only IDs and field "
- "member expressions can be aliased."),
- Log_Alias, expression->firstSourceLocation());
+ m_logger->log(QStringLiteral("Invalid alias expression. Only IDs and field "
+ "member expressions can be aliased."),
+ Log_Alias, expression->firstSourceLocation());
}
} else {
const auto name = publicMember->memberType->name.toString();
@@ -1270,7 +1308,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (publicMember->isRequired())
m_currentScope->setPropertyLocallyRequired(prop.propertyName(), true);
- parseLiteralBinding(publicMember->name.toString(), publicMember->statement);
+ parseLiteralOrScriptBinding(publicMember->name.toString(), publicMember->statement);
break;
}
@@ -1366,8 +1404,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
{
- m_logger->logWarning(u"Declared function \"%1\""_qs.arg(fdecl->name), Log_ControlsSanity,
- fdecl->firstSourceLocation());
+ m_logger->log(u"Declared function \"%1\""_qs.arg(fdecl->name), Log_ControlsSanity,
+ fdecl->firstSourceLocation());
visitFunctionExpressionHelper(fdecl);
return true;
}
@@ -1394,12 +1432,9 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
// ### TODO: add warning about suspicious translation binding when returning false?
-static std::optional<QQmlJSMetaPropertyBinding>
-handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
- const QQmlJSImporter::ImportedTypes &rootScopeImports,
- const QQmlJS::SourceLocation &location)
+void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base,
+ QQmlJS::AST::ArgumentList *args)
{
- std::optional<QQmlJSMetaPropertyBinding> maybeBinding = std::nullopt;
QStringView mainString;
auto registerMainString = [&](QStringView string) {
mainString = string;
@@ -1408,116 +1443,75 @@ handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
auto discardCommentString = [](QStringView) {return -1;};
auto finalizeBinding = [&](QV4::CompiledData::Binding::ValueType type,
QV4::CompiledData::TranslationData) {
- QQmlJSMetaPropertyBinding binding(location);
if (type == QV4::CompiledData::Binding::Type_Translation) {
binding.setTranslation(mainString);
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
binding.setTranslationId(mainString);
} else {
- binding.setLiteral(
- QQmlJSMetaPropertyBinding::StringLiteral, u"string"_qs,
- mainString.toString(), rootScopeImports[u"string"_qs].scope);
+ binding.setStringLiteral(mainString);
}
- maybeBinding = binding;
};
QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString, discardCommentString, finalizeBinding);
- return maybeBinding;
}
-bool QQmlJSImportVisitor::parseLiteralBinding(const QString name,
+QQmlJSImportVisitor::LiteralOrScriptParseResult QQmlJSImportVisitor::parseLiteralOrScriptBinding(const QString name,
const QQmlJS::AST::Statement *statement)
{
const auto *exprStatement = cast<const ExpressionStatement *>(statement);
if (exprStatement == nullptr)
- return false;
-
- QVariant value;
- QString literalType;
- QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid;
+ return LiteralOrScriptParseResult::Invalid;
auto expr = exprStatement->expression;
+ QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
+
switch (expr->kind) {
case Node::Kind_TrueLiteral:
- value = true;
- literalType = u"bool"_qs;
- bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
+ binding.setBoolLiteral(true);
break;
case Node::Kind_FalseLiteral:
- value = false;
- literalType = u"bool"_qs;
- bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
+ binding.setBoolLiteral(false);
break;
case Node::Kind_NullExpression:
- // Note: because we set value to nullptr, Null binding returns false in
- // QQmlJSMetaPropertyBinding::hasLiteral()
- value = QVariant::fromValue(nullptr);
- Q_ASSERT(value.isNull());
- literalType = u"$internal$.std::nullptr_t"_qs;
- bindingType = QQmlJSMetaPropertyBinding::Null;
+ binding.setNullLiteral();
break;
case Node::Kind_NumericLiteral:
- literalType = u"double"_qs;
- value = cast<NumericLiteral *>(expr)->value;
- bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
+ binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
break;
case Node::Kind_StringLiteral:
- literalType = u"string"_qs;
- value = cast<StringLiteral *>(expr)->value.toString();
- bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
+ binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
break;
case Node::Kind_RegExpLiteral:
- literalType = u"regexp"_qs;
- value = cast<RegExpLiteral *>(expr)->pattern.toString();
- bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral;
+ binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
break;
case Node::Kind_TemplateLiteral: {
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
Q_ASSERT(templateLit);
- value = templateLit->value.toString();
if (templateLit->hasNoSubstitution) {
- literalType = u"string"_qs;
- bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
+ binding.setStringLiteral(templateLit->value);
} else {
- bindingType = QQmlJSMetaPropertyBinding::Script;
+ binding.setScriptBinding();
}
break;
}
default:
if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
- if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
- literalType = u"double"_qs;
- bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
- value = -lit->value;
- }
+ if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
+ binding.setNumberLiteral(-lit->value);
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
- if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
- if (auto translationBindingOpt = handleTranslationBinding(
- base->name, call->arguments, m_rootScopeImports,
- expr->firstSourceLocation())) {
- auto translationBinding = translationBindingOpt.value();
- translationBinding.setPropertyName(name);
- m_currentScope->addOwnPropertyBinding(translationBinding);
- if (translationBinding.bindingType() == QQmlJSMetaPropertyBinding::BindingType::StringLiteral)
- m_literalScopesToCheck << m_currentScope;
- return true;
- }
- }
+ if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
+ handleTranslationBinding(binding, base->name, call->arguments);
}
break;
}
-
- if (!QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType))
- return false;
- Q_ASSERT(m_rootScopeImports.contains(literalType)); // built-ins must contain support for all literal bindings
-
- QQmlJSMetaPropertyBinding binding(exprStatement->expression->firstSourceLocation(), name);
- binding.setLiteral(bindingType, literalType, value, m_rootScopeImports[literalType].scope);
-
- m_currentScope->addOwnPropertyBinding(binding);
-
+ if (binding.isValid()) // always add the binding to the scope, even if it's not a literal one
+ m_currentScope->addOwnPropertyBinding(binding);
+ else
+ return LiteralOrScriptParseResult::Invalid;
+ if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
+ return LiteralOrScriptParseResult::Script;
m_literalScopesToCheck << m_currentScope;
- return true;
+ return LiteralOrScriptParseResult::Literal;
}
void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
@@ -1527,10 +1521,12 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
if (const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
return idExpression->name.toString();
else if (const auto *idString = cast<StringLiteral *>(statement->expression)) {
- m_logger->logInfo(u"ids do not need quotation marks"_qs, Log_Syntax, idString->firstSourceLocation());
+ m_logger->log(u"ids do not need quotation marks"_qs, Log_SyntaxIdQuotation,
+ idString->firstSourceLocation());
return idString->value.toString();
}
- m_logger->logWarning(u"Failed to parse id"_qs, Log_Syntax, statement->expression->firstSourceLocation());
+ m_logger->log(u"Failed to parse id"_qs, Log_Syntax,
+ statement->expression->firstSourceLocation());
return QString();
}();
@@ -1542,14 +1538,11 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
auto otherLocation = otherScopeWithID->sourceLocation();
// critical because subsequent analysis cannot cope with messed up ids
// and the file is invalid
- m_logger->logCritical(
- u"Found a duplicated id. id %1 was first declared at %2:%3"_qs.arg(
- name,
- QString::number(otherLocation.startLine),
- QString::number(otherLocation.startColumn)),
- Log_Syntax, // ??
- scriptBinding->firstSourceLocation()
- );
+ m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_qs.arg(
+ name, QString::number(otherLocation.startLine),
+ QString::number(otherLocation.startColumn)),
+ Log_Syntax, // ??
+ scriptBinding->firstSourceLocation());
}
}
if (!name.isEmpty())
@@ -1580,19 +1573,15 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
auto name = group->name;
if (id && id->name.toString() == u"anchors")
- m_logger->logWarning(u"Using anchors here"_qs, Log_ControlsSanity,
- scriptBinding->firstSourceLocation());
+ m_logger->log(u"Using anchors here"_qs, Log_ControlsSanity,
+ scriptBinding->firstSourceLocation());
const auto signal = QQmlJSUtils::signalName(name);
if (!signal.has_value()) {
m_propertyBindings[m_currentScope].append(
{ m_savedBindingOuterScope, group->firstSourceLocation(), name.toString() });
- if (!parseLiteralBinding(name.toString(), scriptBinding->statement)) {
- QQmlJSMetaPropertyBinding binding(group->firstSourceLocation(), name.toString());
- // TODO: Actually store the value
- m_savedBindingOuterScope->addOwnPropertyBinding(binding);
- }
+ parseLiteralOrScriptBinding(name.toString(), scriptBinding->statement);
} else {
const auto statement = scriptBinding->statement;
QStringList signalParameters;
@@ -1604,8 +1593,8 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
}
}
- m_logger->logWarning(u"Declared signal handler \"%1\""_qs.arg(name), Log_ControlsSanity,
- scriptBinding->firstSourceLocation());
+ m_logger->log(u"Declared signal handler \"%1\""_qs.arg(name), Log_ControlsSanity,
+ scriptBinding->firstSourceLocation());
m_signals[m_currentScope].append({ m_savedBindingOuterScope, group->firstSourceLocation(),
qMakePair(name.toString(), signalParameters) });
@@ -1803,10 +1792,14 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
{
- // If a file uses pragma Strict it expects to be compiled, so automatically enable compiler
- // warnings unless the user has explicitly set the level.
- if (pragma->name == u"Strict"_qs && !m_logger->wasCategoryChanged(Log_Compiler))
+ // If a file uses pragma Strict, it expects to be compiled, so automatically
+ // enable compiler warnings unless the level is set explicitly already (e.g.
+ // by the user).
+ if (pragma->name == u"Strict"_qs && !m_logger->wasCategoryChanged(Log_Compiler)) {
+ // TODO: the logic here is rather complicated and may be buggy
m_logger->setCategoryLevel(Log_Compiler, QtWarningMsg);
+ m_logger->setCategoryIgnored(Log_Compiler, false);
+ }
if (pragma->name == u"Singleton")
m_rootIsSingleton = true;
@@ -1816,8 +1809,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
void QQmlJSImportVisitor::throwRecursionDepthError()
{
- m_logger->logCritical(QStringLiteral("Maximum statement or expression depth exceeded"),
- Log_RecursionDepthError);
+ m_logger->log(QStringLiteral("Maximum statement or expression depth exceeded"),
+ Log_RecursionDepthError, QQmlJS::SourceLocation());
}
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
@@ -1906,11 +1899,10 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("with"),
ast->firstSourceLocation());
- m_logger->logWarning(
- QStringLiteral("with statements are strongly discouraged in QML "
- "and might cause false positives when analysing unqualified "
- "identifiers"),
- Log_WithStatement, ast->firstSourceLocation());
+ m_logger->log(QStringLiteral("with statements are strongly discouraged in QML "
+ "and might cause false positives when analysing unqualified "
+ "identifiers"),
+ Log_WithStatement, ast->firstSourceLocation());
return true;
}
@@ -2031,7 +2023,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
}
if (foundIds) {
- m_logger->logWarning(
+ m_logger->log(
u"Cannot defer property assignment to \"%1\". Assigning an id to an object or one of its sub-objects bound to a deferred property will make the assignment immediate."_qs
.arg(propertyName),
Log_DeferredPropertyId, uiob->firstSourceLocation());
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 4a006c9cde..e932bb5b2f 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -203,7 +203,8 @@ protected:
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
QQmlJSLogger *m_logger;
- bool parseLiteralBinding(const QString name, const QQmlJS::AST::Statement *statement);
+ enum class LiteralOrScriptParseResult { Invalid, Script, Literal };
+ LiteralOrScriptParseResult parseLiteralOrScriptBinding(const QString name, const QQmlJS::AST::Statement *statement);
// Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index c50740750e..4d3eb8e9b5 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -56,7 +56,7 @@ public:
QQmlJS::SourceLocation accessLocation) override
{
Q_UNUSED(fileName)
- m_logger->logWarning(
+ m_logger->log(
u"Variable \"%1\" is used here before its declaration. The declaration is at %2:%3."_qs
.arg(name)
.arg(declarationLocation.startLine)
@@ -69,7 +69,7 @@ private:
};
QQmlJSLinter::QQmlJSLinter(const QStringList &importPaths, bool useAbsolutePath)
- : m_useAbsolutePath(useAbsolutePath), m_importer(importPaths, nullptr)
+ : m_useAbsolutePath(useAbsolutePath), m_importer(importPaths, nullptr, true)
{
}
@@ -101,8 +101,8 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
if (option != logger->options().constEnd())
categories << option->m_category;
else
- logger->logWarning(u"qmllint directive on unknown category \"%1\""_qs.arg(category),
- Log_Syntax, loc);
+ logger->log(u"qmllint directive on unknown category \"%1\""_qs.arg(category),
+ Log_Syntax, loc);
}
if (categories.isEmpty()) {
@@ -129,8 +129,8 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
} else if (command == u"enable"_qs) {
enablesPerLine[loc.startLine + 1] |= categories;
} else {
- logger->logWarning(u"Invalid qmllint directive \"%1\" provided"_qs.arg(command),
- Log_Syntax, loc);
+ logger->log(u"Invalid qmllint directive \"%1\" provided"_qs.arg(command), Log_Syntax,
+ loc);
}
}
@@ -221,6 +221,7 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
jsonFix[u"charOffset"_qs] = static_cast<int>(fix.cutLocation.offset);
jsonFix[u"length"_qs] = static_cast<int>(fix.cutLocation.length);
jsonFix[u"replacement"_qs] = fix.replacementString;
+ jsonFix[u"isHint"] = fix.isHint;
suggestions << jsonFix;
}
}
@@ -314,7 +315,7 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
if (!it.value().m_changed)
continue;
- m_logger->setCategoryError(it.value().m_category, it.value().m_error);
+ m_logger->setCategoryIgnored(it.value().m_category, it.value().m_ignored);
m_logger->setCategoryLevel(it.value().m_category, it.value().m_level);
}
@@ -361,10 +362,9 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
QList<QQmlJS::DiagnosticMessage> warnings = m_importer.takeGlobalWarnings();
if (!warnings.isEmpty()) {
- m_logger->logWarning(
- QStringLiteral("Type warnings occurred while evaluating file:"),
- Log_Import);
- m_logger->processMessages(warnings, QtWarningMsg, Log_Import);
+ m_logger->log(QStringLiteral("Type warnings occurred while evaluating file:"),
+ Log_Import, QQmlJS::SourceLocation());
+ m_logger->processMessages(warnings, Log_Import);
}
success &= !m_logger->hasWarnings() && !m_logger->hasErrors();
diff --git a/src/qmlcompiler/qqmljsloadergenerator.cpp b/src/qmlcompiler/qqmljsloadergenerator.cpp
index ffe4774266..f0e28895e9 100644
--- a/src/qmlcompiler/qqmljsloadergenerator.cpp
+++ b/src/qmlcompiler/qqmljsloadergenerator.cpp
@@ -125,6 +125,8 @@ bool qQmlJSGenerateLoader(const QStringList &compiledFiles, const QString &outpu
stream << "#include <QtQml/qqmlprivate.h>\n";
stream << "#include <QtCore/qdir.h>\n";
stream << "#include <QtCore/qurl.h>\n";
+ stream << "#include <QtCore/qhash.h>\n";
+ stream << "#include <QtCore/qstring.h>\n";
stream << "\n";
stream << "namespace QmlCacheGeneratedCode {\n";
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index d7bdc714a6..ce2f559c1a 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -28,16 +28,19 @@
#include "qqmljslogger_p.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+
QT_BEGIN_NAMESPACE
const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
static QMap<QString, QQmlJSLogger::Option> optionsMap = {
{ QStringLiteral("required"),
QQmlJSLogger::Option(Log_Required, QStringLiteral("RequiredProperty"),
- QStringLiteral("Warn about required properties"), QtInfoMsg) },
+ QStringLiteral("Warn about required properties"), QtWarningMsg) },
{ QStringLiteral("alias"),
QQmlJSLogger::Option(Log_Alias, QStringLiteral("PropertyAlias"),
- QStringLiteral("Warn about alias errors"), QtInfoMsg) },
+ QStringLiteral("Warn about alias errors"), QtWarningMsg) },
{ QStringLiteral("import"),
QQmlJSLogger::Option(Log_Import, QStringLiteral("ImportFailure"),
QStringLiteral("Warn about failing imports and deprecated qmltypes"),
@@ -49,7 +52,7 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("inheritance-cycle"),
QQmlJSLogger::Option(Log_InheritanceCycle, QStringLiteral("InheritanceCycle"),
- QStringLiteral("Warn about inheritance cycles"), QtInfoMsg) },
+ QStringLiteral("Warn about inheritance cycles"), QtWarningMsg) },
{ QStringLiteral("deprecated"),
QQmlJSLogger::Option(Log_Deprecation, QStringLiteral("Deprecated"),
QStringLiteral("Warn about deprecated properties and types"),
@@ -64,13 +67,13 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("property"),
QQmlJSLogger::Option(Log_Property, QStringLiteral("UnknownProperty"),
- QStringLiteral("Warn about unknown properties"), QtInfoMsg) },
+ QStringLiteral("Warn about unknown properties"), QtWarningMsg) },
{ QStringLiteral("deferred-property-id"),
QQmlJSLogger::Option(
Log_DeferredPropertyId, QStringLiteral("DeferredPropertyId"),
QStringLiteral(
"Warn about making deferred properties immediate by giving them an id."),
- QtInfoMsg) },
+ QtWarningMsg) },
{ QStringLiteral("unqualified"),
QQmlJSLogger::Option(
Log_UnqualifiedAccess, QStringLiteral("UnqualifiedAccess"),
@@ -78,24 +81,23 @@ const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
QtWarningMsg) },
{ QStringLiteral("unused-imports"),
QQmlJSLogger::Option(Log_UnusedImport, QStringLiteral("UnusedImports"),
- QStringLiteral("Warn about unused imports"), QtInfoMsg, false) },
+ QStringLiteral("Warn about unused imports"), QtInfoMsg) },
{ QStringLiteral("multiline-strings"),
QQmlJSLogger::Option(Log_MultilineString, QStringLiteral("MultilineStrings"),
- QStringLiteral("Warn about multiline strings"), QtInfoMsg, false) },
+ QStringLiteral("Warn about multiline strings"), QtInfoMsg) },
{ QStringLiteral("compiler"),
QQmlJSLogger::Option(Log_Compiler, QStringLiteral("CompilerWarnings"),
- QStringLiteral("Warn about compiler issues"), QtCriticalMsg,
- false) },
+ QStringLiteral("Warn about compiler issues"), QtCriticalMsg, true) },
{ QStringLiteral("controls-sanity"),
QQmlJSLogger::Option(
Log_ControlsSanity, QStringLiteral("ControlsSanity"),
QStringLiteral("Performance checks used for QuickControl's implementation"),
- QtCriticalMsg, false) },
+ QtCriticalMsg, true) },
{ QStringLiteral("multiple-attached-objects"),
QQmlJSLogger::Option(
Log_AttachedPropertyReuse, QStringLiteral("AttachedPropertyReuse"),
QStringLiteral("Warn if attached types from parent components aren't reused"),
- QtCriticalMsg, false) }
+ QtCriticalMsg, true) }
};
return optionsMap;
@@ -106,20 +108,20 @@ QQmlJSLogger::QQmlJSLogger()
const auto &opt = options();
for (auto it = opt.cbegin(); it != opt.cend(); ++it) {
m_categoryLevels[it.value().m_category] = it.value().m_level;
- m_categoryError[it.value().m_category] = it.value().m_error;
+ m_categoryIgnored[it.value().m_category] = it.value().m_ignored;
}
// These have to be set up manually since we don't expose it as an option
- m_categoryLevels[Log_RecursionDepthError] = QtInfoMsg;
- m_categoryError[Log_RecursionDepthError] = true;
- m_categoryLevels[Log_Syntax] = QtInfoMsg;
- m_categoryError[Log_Syntax] = true;
+ m_categoryLevels[Log_RecursionDepthError] = QtCriticalMsg;
+ m_categoryLevels[Log_Syntax] = QtWarningMsg; // TODO: because we usually report it as a warning!
+ m_categoryLevels[Log_SyntaxIdQuotation] = QtWarningMsg;
+ m_categoryLevels[Log_SyntaxDuplicateIds] = QtCriticalMsg;
// setup color output
m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground);
- m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground);
+ m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow?
m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground);
- m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground);
+ m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None?
}
static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
@@ -134,18 +136,22 @@ static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
- bool showFileName, const std::optional<FixSuggestion> &suggestion)
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName)
{
- if (isMsgTypeLess(type, m_categoryLevels[category]))
+ if (isCategoryIgnored(category))
return;
+ // Note: assume \a type is the type we should prefer for logging
+
if (srcLocation.isValid() && m_ignoredWarnings[srcLocation.startLine].contains(category))
return;
QString prefix;
- if (!m_fileName.isEmpty() && showFileName)
- prefix = m_fileName + QStringLiteral(":");
+ if ((!overrideFileName.isEmpty() || !m_fileName.isEmpty()) && showFileName)
+ prefix =
+ (!overrideFileName.isEmpty() ? overrideFileName : m_fileName) + QStringLiteral(":");
if (srcLocation.isValid())
prefix += QStringLiteral("%1:%2:").arg(srcLocation.startLine).arg(srcLocation.startColumn);
@@ -153,26 +159,21 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
if (!prefix.isEmpty())
prefix.append(QLatin1Char(' '));
- m_output.writePrefixedMessage(prefix + message, type);
-
- QtMsgType machineType = isMsgTypeLess(QtWarningMsg, type) ? QtCriticalMsg : QtInfoMsg;
+ // Note: we do the clamping to [Info, Critical] range since our logger only
+ // supports 3 categories
+ type = std::clamp(type, QtInfoMsg, QtCriticalMsg, isMsgTypeLess);
- // If this is a category that produces error codes, we need to up all the messages to at least a
- // warning level
- if (isCategoryError(category)) {
- if (isMsgTypeLess(type, QtWarningMsg))
- machineType = QtWarningMsg;
- else
- machineType = type;
- }
+ // Note: since we clamped our \a type, the output message is not printed
+ // exactly like it was requested, bear with us
+ m_output.writePrefixedMessage(prefix + message, type);
Message diagMsg;
diagMsg.message = message;
diagMsg.loc = srcLocation;
- diagMsg.type = machineType;
+ diagMsg.type = type;
diagMsg.fixSuggestion = suggestion;
- switch (machineType) {
+ switch (type) {
case QtWarningMsg: m_warnings.push_back(diagMsg); break;
case QtCriticalMsg: m_errors.push_back(diagMsg); break;
case QtInfoMsg: m_infos.push_back(diagMsg); break;
@@ -180,29 +181,41 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
}
if (srcLocation.isValid() && !m_code.isEmpty() && showContext)
- printContext(srcLocation);
+ printContext(overrideFileName, srcLocation);
if (suggestion.has_value())
printFix(suggestion.value());
}
void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
- QtMsgType level, QQmlJSLoggerCategory category)
+ QQmlJSLoggerCategory category)
{
- if (isMsgTypeLess(level, m_categoryLevels[category]) || messages.isEmpty())
+ if (messages.isEmpty() || isCategoryIgnored(category))
return;
m_output.write(QStringLiteral("---\n"));
+ // TODO: we should instead respect message's category here (potentially, it
+ // should hold a category instead of type)
for (const QQmlJS::DiagnosticMessage &message : messages)
- logWarning(message.message, category, QQmlJS::SourceLocation(), false, false);
+ log(message.message, category, QQmlJS::SourceLocation(), false, false);
m_output.write(QStringLiteral("---\n\n"));
}
-void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
+void QQmlJSLogger::printContext(const QString &overrideFileName,
+ const QQmlJS::SourceLocation &location)
{
- IssueLocationWithContext issueLocationWithContext { m_code, location };
+ QString code = m_code;
+
+ if (!overrideFileName.isEmpty() && overrideFileName != QFileInfo(m_fileName).absolutePath()) {
+ QFile file(overrideFileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, location };
if (const QStringView beforeText = issueLocationWithContext.beforeText(); !beforeText.isEmpty())
m_output.write(beforeText);
@@ -226,28 +239,56 @@ void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
void QQmlJSLogger::printFix(const FixSuggestion &fix)
{
+ const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
+ QString code = m_code;
+ QString currentFile;
for (const auto &fixItem : fix.fixes) {
m_output.writePrefixedMessage(fixItem.message, QtInfoMsg);
if (!fixItem.cutLocation.isValid())
continue;
- IssueLocationWithContext issueLocationWithContext { m_code, fixItem.cutLocation };
+ if (fixItem.fileName == currentFile) {
+ // Nothing to do in this case, we've already read the code
+ } else if (fixItem.fileName.isEmpty() || fixItem.fileName == currentFileAbsPath) {
+ code = m_code;
+ } else {
+ QFile file(fixItem.fileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ currentFile = fixItem.fileName;
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, fixItem.cutLocation };
if (const QStringView beforeText = issueLocationWithContext.beforeText();
!beforeText.isEmpty()) {
m_output.write(beforeText);
}
- m_output.write(fixItem.replacementString, QtDebugMsg);
+ // The replacement string can be empty if we're only pointing something out to the user
+ QStringView replacementString = fixItem.replacementString.isEmpty()
+ ? issueLocationWithContext.issueText()
+ : fixItem.replacementString;
+
+ // But if there's nothing to change it has to be a hint
+ if (fixItem.replacementString.isEmpty())
+ Q_ASSERT(fixItem.isHint);
+
+ m_output.write(replacementString, QtDebugMsg);
m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
int tabCount = issueLocationWithContext.beforeText().count(u'\t');
+
+ // Do not draw location indicator for multiline replacement strings
+ if (replacementString.contains(u'\n'))
+ continue;
+
m_output.write(u" "_qs.repeated(
issueLocationWithContext.beforeText().length() - tabCount)
+ u"\t"_qs.repeated(tabCount)
- + u"^"_qs.repeated(fixItem.replacementString.length())
- + u'\n');
+ + u"^"_qs.repeated(fixItem.replacementString.length()) + u'\n');
}
}
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index 0792cd434b..2ddcb2ac5a 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -107,6 +107,8 @@ enum QQmlJSLoggerCategory {
Log_UnusedImport,
Log_MultilineString,
Log_Syntax,
+ Log_SyntaxIdQuotation,
+ Log_SyntaxDuplicateIds,
Log_Compiler,
Log_ControlsSanity,
Log_AttachedPropertyReuse,
@@ -120,6 +122,10 @@ struct FixSuggestion
QString message;
QQmlJS::SourceLocation cutLocation = QQmlJS::SourceLocation();
QString replacementString = QString();
+ QString fileName = QString();
+ // A Fix is a hint if it can not be automatically applied to fix an issue or only points out
+ // its origin
+ bool isHint = true;
};
QList<Fix> fixes;
};
@@ -137,31 +143,32 @@ public:
{
Option() = default;
Option(QQmlJSLoggerCategory category, QString settingsName, const QString &description,
- QtMsgType level, bool error = true)
+ QtMsgType level, bool ignored = false)
: m_category(category),
m_settingsName(settingsName),
m_description(description),
m_level(level),
- m_error(error)
+ m_ignored(ignored)
{
}
QQmlJSLoggerCategory m_category;
QString m_settingsName;
QString m_description;
QtMsgType m_level;
- bool m_error;
+ bool m_ignored;
bool m_changed = false;
QString levelToString() const {
+ // TODO:: this only makes sense to qmllint
+ Q_ASSERT(m_ignored || m_level != QtCriticalMsg);
+ if (m_ignored)
+ return QStringLiteral("disable");
+
switch (m_level) {
case QtInfoMsg:
- return m_error ? QStringLiteral("warning") : QStringLiteral("info");
+ return QStringLiteral("info");
case QtWarningMsg:
- // TODO: This case doesn't cleanly map onto any warning level yet
- // this has to be handled in the option overhaul
- return m_error ? QStringLiteral("warning") : QStringLiteral("info");
- case QtCriticalMsg:
- return QStringLiteral("disable");
+ return QStringLiteral("warning");
default:
Q_UNREACHABLE();
break;
@@ -170,14 +177,14 @@ public:
bool setLevel(const QString &level) {
if (level == QStringLiteral("disable")) {
- m_level = QtCriticalMsg;
- m_error = false;
+ m_level = QtCriticalMsg; // TODO: only so for consistency with previous logic
+ m_ignored = true;
} else if (level == QStringLiteral("info")) {
m_level = QtInfoMsg;
- m_error = false;
+ m_ignored = false;
} else if (level == QStringLiteral("warning")) {
- m_level = QtInfoMsg;
- m_error = true;
+ m_level = QtWarningMsg;
+ m_ignored = false;
} else {
return false;
}
@@ -206,10 +213,13 @@ public:
m_categoryChanged[category] = true;
}
- bool isCategoryError(QQmlJSLoggerCategory category) const { return m_categoryError[category]; }
- void setCategoryError(QQmlJSLoggerCategory category, bool error)
+ bool isCategoryIgnored(QQmlJSLoggerCategory category) const
+ {
+ return m_categoryIgnored[category];
+ }
+ void setCategoryIgnored(QQmlJSLoggerCategory category, bool error)
{
- m_categoryError[category] = error;
+ m_categoryIgnored[category] = error;
m_categoryChanged[category] = true;
}
@@ -218,31 +228,23 @@ public:
return m_categoryChanged[category];
}
- void logInfo(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
- {
- log(message, category, srcLocation, QtInfoMsg, showContext, showFileName, suggestion);
- }
+ /*! \internal
- void logWarning(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
- {
- log(message, category, srcLocation, QtWarningMsg, showContext, showFileName, suggestion);
- }
+ Logs \a message with severity deduced from \a category. Prefer using
+ this function in most cases.
- void logCritical(const QString &message, QQmlJSLoggerCategory category,
- const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation(),
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {})
+ \sa setCategoryLevel
+ */
+ void log(const QString &message, QQmlJSLoggerCategory category,
+ const QQmlJS::SourceLocation &srcLocation, bool showContext = true,
+ bool showFileName = true, const std::optional<FixSuggestion> &suggestion = {},
+ const QString overrideFileName = QString())
{
- log(message, category, srcLocation, QtCriticalMsg, showContext, showFileName, suggestion);
+ log(message, category, srcLocation, m_categoryLevels[category], showContext, showFileName,
+ suggestion, overrideFileName);
}
- void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages, QtMsgType level,
+ void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
QQmlJSLoggerCategory category);
void ignoreWarnings(uint32_t line, const QSet<QQmlJSLoggerCategory> &categories)
@@ -260,12 +262,13 @@ public:
QString fileName() const { return m_fileName; }
private:
- void printContext(const QQmlJS::SourceLocation &location);
+ void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location);
void printFix(const FixSuggestion &fix);
- void log(const QString &message, QQmlJSLoggerCategory category, const QQmlJS::SourceLocation &,
- QtMsgType type, bool showContext, bool showFileName,
- const std::optional<FixSuggestion> &suggestion);
+ void log(const QString &message, QQmlJSLoggerCategory category,
+ const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName);
QString m_fileName;
QString m_code;
@@ -273,13 +276,16 @@ private:
QColorOutput m_output;
QtMsgType m_categoryLevels[QQmlJSLoggerCategory_Last + 1] = {};
- bool m_categoryError[QQmlJSLoggerCategory_Last + 1] = {};
+ bool m_categoryIgnored[QQmlJSLoggerCategory_Last + 1] = {};
bool m_categoryChanged[QQmlJSLoggerCategory_Last + 1] = {};
QList<Message> m_infos;
QList<Message> m_warnings;
QList<Message> m_errors;
QHash<uint32_t, QSet<QQmlJSLoggerCategory>> m_ignoredWarnings;
+
+ // the compiler needs private log() function at the moment
+ friend class QQmlJSAotCompiler;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp
new file mode 100644
index 0000000000..2d85343746
--- /dev/null
+++ b/src/qmlcompiler/qqmljsmetatypes.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 <private/qqmljsmetatypes_p.h>
+#include <private/qqmljstyperesolver_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ A binding is valid when it has both a target (m_propertyName is set)
+ and some content set (m_bindingType != Invalid).
+*/
+bool QQmlJSMetaPropertyBinding::isValid() const { return !m_propertyName.isEmpty() && bindingType() != Invalid; }
+
+QString QQmlJSMetaPropertyBinding::literalTypeName() const
+{
+ if (std::holds_alternative<Content::BoolLiteral>(m_bindingContent))
+ return QLatin1String("bool");
+ else if (std::holds_alternative<Content::NumberLiteral>(m_bindingContent))
+ return QLatin1String("double");
+ else if (std::holds_alternative<Content::StringLiteral>(m_bindingContent))
+ return QLatin1String("string");
+ else if (std::holds_alternative<Content::RegexpLiteral>(m_bindingContent))
+ return QLatin1String("regexp");
+ else if (std::holds_alternative<Content::Null>(m_bindingContent))
+ return QLatin1String("$internal$.std::nullptr_t");
+ return {};
+}
+
+bool QQmlJSMetaPropertyBinding::boolValue() const
+{
+ if (auto boolLit = std::get_if<Content::BoolLiteral>(&m_bindingContent))
+ return boolLit->value;
+ // warn
+ return false;
+}
+
+double QQmlJSMetaPropertyBinding::numberValue() const
+{
+ if (auto numberLit = std::get_if<Content::NumberLiteral>(&m_bindingContent))
+ return numberLit->value;
+ // warn
+ return 0;
+}
+
+QString QQmlJSMetaPropertyBinding::stringValue() const
+{
+ if (auto stringLiteral = std::get_if<Content::StringLiteral>(&m_bindingContent))
+ return stringLiteral->value;
+ // warn
+ return {};
+}
+
+/*!
+ \internal
+ Uses \a resolver to return the correct type for the stored literal
+ and a null scope pointer if the binding does not contain a literal
+ */
+QSharedPointer<const QQmlJSScope> QQmlJSMetaPropertyBinding::literalType(const QQmlJSTypeResolver *resolver) const
+{
+ Q_ASSERT(resolver);
+ switch (bindingType()) {
+ case QQmlJSMetaPropertyBinding::BoolLiteral:
+ return resolver->boolType();
+ case QQmlJSMetaPropertyBinding::NumberLiteral:
+ return resolver->typeForName(QLatin1String("double"));
+ case QQmlJSMetaPropertyBinding::Translation: // translations are strings
+ case QQmlJSMetaPropertyBinding::TranslationById:
+ case QQmlJSMetaPropertyBinding::StringLiteral:
+ return resolver->stringType();
+ case QQmlJSMetaPropertyBinding::RegExpLiteral:
+ return resolver->typeForName(QLatin1String("regexp"));
+ case QQmlJSMetaPropertyBinding::Null:
+ return resolver->nullType();
+ default:
+ return {};
+ }
+ Q_UNREACHABLE();
+ return {}; // needed on some compilers which do not see that every case in the switch returns
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 54627f9e35..76ec93e46b 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -59,6 +59,7 @@
QT_BEGIN_NAMESPACE
+class QQmlJSTypeResolver;
class QQmlJSScope;
class QQmlJSMetaEnum
{
@@ -390,38 +391,98 @@ public:
};
private:
- QQmlJS::SourceLocation m_sourceLocation;
- QString m_propertyName; // TODO: this is a debug-only information
- BindingType m_bindingType = BindingType::Invalid;
-
- // TODO: the below should really be put into a union (of sorts). despite the
- // data being overlapping for different things (which for now is just
- // ignored), we would still only have one kind of information stored in a
- // binding. separating the non-overlapping bits would indicate what we
- // require for each binding type more clearly
-
- //union {
- QString m_translationString;
- QString m_translationId;
- QVariant m_literalValue; // constant in literal (or null) expression
- //};
- QWeakPointer<const QQmlJSScope> m_value; // object type of Object binding *OR* a literal type
- QString m_valueTypeName;
-
- QWeakPointer<const QQmlJSScope> m_interceptor; // QQmlPropertyValueInterceptor derived type
- QString m_interceptorTypeName;
+ // needs to be kept in sync with the BindingType enum
+ struct Content {
+ using Invalid = std::monostate;
+ struct BoolLiteral {
+ bool value;
+ friend bool operator==(BoolLiteral a, BoolLiteral b) { return a.value == b.value; }
+ friend bool operator!=(BoolLiteral a, BoolLiteral b) { return !(a == b); }
+ };
+ struct NumberLiteral {
+ friend bool operator==(NumberLiteral a, NumberLiteral b) { return a.value == b.value; }
+ friend bool operator!=(NumberLiteral a, NumberLiteral b) { return !(a == b); }
+ double value; // ### TODO: int?
+ };
+ struct StringLiteral {
+ friend bool operator==(StringLiteral a, StringLiteral b) { return a.value == b.value; }
+ friend bool operator!=(StringLiteral a, StringLiteral b) { return !(a == b); }
+ QString value;
+ };
+ struct RegexpLiteral {
+ friend bool operator==(RegexpLiteral a, RegexpLiteral b) { return a.value == b.value; }
+ friend bool operator!=(RegexpLiteral a, RegexpLiteral b) { return !(a == b); }
+ QString value;
+ };
+ struct Null {
+ friend bool operator==(Null , Null ) { return true; }
+ friend bool operator!=(Null a, Null b) { return !(a == b); }
+ };
+ struct TranslationString {
+ friend bool operator==(TranslationString a, TranslationString b) { return a.value == b.value; }
+ friend bool operator!=(TranslationString a, TranslationString b) { return !(a == b); }
+ QString value;
+ };
+ struct TranslationById {
+ friend bool operator==(TranslationById a, TranslationById b) { return a.value == b.value; }
+ friend bool operator!=(TranslationById a, TranslationById b) { return !(a == b); }
+ QString value;
+ };
+ struct Script {
+ friend bool operator==(Script , Script ) { return true; }
+ friend bool operator!=(Script a, Script b) { return !(a == b); }
+ };
+ struct Object {
+ friend bool operator==(Object a, Object b) { return a.value == b.value && a.typeName == b.typeName; }
+ friend bool operator!=(Object a, Object b) { return !(a == b); }
+ QString typeName;
+ QWeakPointer<const QQmlJSScope> value;
+ };
+ struct Interceptor {
+ friend bool operator==(Interceptor a, Interceptor b)
+ {
+ return a.interceptor == b.interceptor && a.interceptorTypeName == b.interceptorTypeName;
+ }
+ friend bool operator!=(Interceptor a, Interceptor b) { return !(a == b); }
+ QString interceptorTypeName;
+ QWeakPointer<const QQmlJSScope> interceptor;
+ };
+ struct ValueSource {
+ friend bool operator==(ValueSource a, ValueSource b)
+ {
+ return a.valueSource == b.valueSource && a.valueSourceTypeName == b.valueSourceTypeName;
+ }
+ friend bool operator!=(ValueSource a, ValueSource b) { return !(a == b); }
+ QString valueSourceTypeName;
+ QWeakPointer<const QQmlJSScope> valueSource;
+ };
+ struct AttachedProperty {
+ friend bool operator==(AttachedProperty , AttachedProperty ) { return true; }
+ friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
+ };
+ struct GroupProperty {
+ friend bool operator==(GroupProperty , GroupProperty ) { return true; }
+ friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); }
+ };
+ using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
+ RegexpLiteral, Null, TranslationString,
+ TranslationById, Script, Object, Interceptor,
+ ValueSource, AttachedProperty, GroupProperty
+ >;
+ };
+ using BindingContent = Content::type;
- QWeakPointer<const QQmlJSScope> m_valueSource; // QQmlPropertyValueSource derived type
- QString m_valueSourceTypeName;
+ QQmlJS::SourceLocation m_sourceLocation;
+ QString m_propertyName; // TODO: this is a debug-only information
+ BindingContent m_bindingContent;
- void setBindingTypeOnce(BindingType type)
+ void ensureSetBindingTypeOnce()
{
- Q_ASSERT(m_bindingType == BindingType::Invalid);
- m_bindingType = type;
+ Q_ASSERT(bindingType() == BindingType::Invalid);
}
- bool isLiteralBinding() const { return isLiteralBinding(m_bindingType); }
+ bool isLiteralBinding() const { return isLiteralBinding(bindingType()); }
public:
@@ -448,90 +509,124 @@ public:
const QQmlJS::SourceLocation &sourceLocation() const { return m_sourceLocation; }
- BindingType bindingType() const { return m_bindingType; }
+ BindingType bindingType() const { return BindingType(m_bindingContent.index()); }
- bool isValid() const { return !m_propertyName.isEmpty(); }
+ bool isValid() const;
+
+ void setStringLiteral(QAnyStringView value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::StringLiteral { value.toString() };
+ }
+
+ void setScriptBinding()
+ {
+ // ### TODO: this does not allow us to do anything interesting with the binding
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Script {};
+ }
- void setLiteral(BindingType kind, const QString &typeName, const QVariant &value,
- const QSharedPointer<const QQmlJSScope> &type)
+ void setBoolLiteral(bool value)
{
- Q_ASSERT(isLiteralBinding(kind));
- setBindingTypeOnce(kind);
- m_value = type;
- m_valueTypeName = typeName;
- m_literalValue = value;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::BoolLiteral { value };
+ }
+
+ void setNullLiteral()
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Null {};
+ }
+
+ void setNumberLiteral(double value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::NumberLiteral { value };
+ }
+
+ void setRegexpLiteral(QAnyStringView value)
+ {
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::RegexpLiteral { value.toString() };
}
// ### TODO: we might need comment and translation number at some point
void setTranslation(QStringView translation)
{
- setBindingTypeOnce(BindingType::Translation);
- m_translationString = translation.toString();
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::TranslationString { translation.toString() };
}
void setTranslationId(QStringView id)
{
- setBindingTypeOnce(BindingType::TranslationById);
- m_translationId = id.toString();
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::TranslationById { id.toString() };
}
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::Object);
- m_value = type;
- m_valueTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Object { typeName, type };
}
void setInterceptor(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::Interceptor);
- m_interceptor = type;
- m_interceptorTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::Interceptor { typeName, type };
}
void setValueSource(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
{
- setBindingTypeOnce(BindingType::ValueSource);
- m_valueSource = type;
- m_valueSourceTypeName = typeName;
+ ensureSetBindingTypeOnce();
+ m_bindingContent = Content::ValueSource { typeName, type };
}
- QString literalTypeName() const { return m_valueTypeName; }
- const QVariant &literalValue() const { return m_literalValue; }
- QSharedPointer<const QQmlJSScope> literalType() const { return m_value; }
+ QString literalTypeName() const;
+
+ // ### TODO: here and below: Introduce an allowConversion parameter, if yes, enable conversions e.g. bool -> number?
+ bool boolValue() const;
- QString objectTypeName() const { return m_valueTypeName; }
- QSharedPointer<const QQmlJSScope> objectType() const { return m_value; }
+ double numberValue() const;
- QString interceptorTypeName() const { return m_interceptorTypeName; }
- QSharedPointer<const QQmlJSScope> interceptorType() const { return m_interceptor; }
+ QString stringValue() const;
- QString valueSourceTypeName() const { return m_valueSourceTypeName; }
- QSharedPointer<const QQmlJSScope> valueSourceType() const { return m_valueSource; }
+ QSharedPointer<const QQmlJSScope> literalType(const QQmlJSTypeResolver *resolver) const;
+
+ QString objectTypeName() const
+ {
+ if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
+ return object->typeName;
+ // warn
+ return {};
+ }
+ QSharedPointer<const QQmlJSScope> objectType() const
+ {
+ if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
+ return object->value.lock();
+ // warn
+ return {};
+ }
bool hasLiteral() const
{
- return isLiteralBinding()
- && (!m_literalValue.isNull() || m_bindingType == QQmlJSMetaPropertyBinding::Null);
+ // TODO: Assumption: if the type is literal, we must have one
+ return isLiteralBinding();
}
- bool hasObject() const { return m_bindingType == BindingType::Object && !m_value.isNull(); }
+ bool hasObject() const { return bindingType() == BindingType::Object; }
bool hasInterceptor() const
{
- return m_bindingType == BindingType::Interceptor && !m_interceptor.isNull();
+ return bindingType() == BindingType::Interceptor;
}
bool hasValueSource() const
{
- return m_bindingType == BindingType::ValueSource && !m_valueSource.isNull();
+ return bindingType() == BindingType::ValueSource;
}
friend bool operator==(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
{
- return a.m_bindingType == b.m_bindingType && a.m_propertyName == b.m_propertyName
- && a.m_valueTypeName == b.m_valueTypeName
- && a.m_interceptorTypeName == b.m_interceptorTypeName
- && a.m_valueSourceTypeName == b.m_valueSourceTypeName
- && a.m_literalValue == b.m_literalValue && a.m_value == b.m_value
- && a.m_interceptor == b.m_interceptor && a.m_sourceLocation == b.m_sourceLocation;
+ return a.m_propertyName == b.m_propertyName
+ && a.m_bindingContent == b.m_bindingContent
+ && a.m_sourceLocation == b.m_sourceLocation;
}
friend bool operator!=(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
@@ -541,12 +636,9 @@ public:
friend size_t qHash(const QQmlJSMetaPropertyBinding &binding, size_t seed = 0)
{
- return qHashMulti(seed, binding.m_propertyName, binding.m_valueTypeName,
- binding.m_interceptorTypeName, binding.m_valueSourceTypeName,
- binding.m_literalValue.toString(), binding.m_value.toStrongRef().data(),
- binding.m_interceptor.toStrongRef().data(),
- binding.m_valueSource.toStrongRef().data(), binding.m_sourceLocation,
- binding.m_bindingType);
+ // we don't need to care about the actual binding content when hashing
+ return qHashMulti(seed, binding.m_propertyName, binding.m_sourceLocation,
+ binding.bindingType());
}
};
diff --git a/src/qmlcompiler/qqmljsregistercontent.cpp b/src/qmlcompiler/qqmljsregistercontent.cpp
index aef0453eac..57d101cf16 100644
--- a/src/qmlcompiler/qqmljsregistercontent.cpp
+++ b/src/qmlcompiler/qqmljsregistercontent.cpp
@@ -36,9 +36,9 @@ QString QQmlJSRegisterContent::descriptiveName() const
QString result = m_storedType->internalName() + u" of "_qs;
const auto scope = [this]() -> QString {
return (m_scope->internalName().isEmpty()
- ? (m_scope->fileName().isEmpty()
+ ? (m_scope->filePath().isEmpty()
? u"??"_qs
- : (u"(component in "_qs + m_scope->fileName() + u")"_qs))
+ : (u"(component in "_qs + m_scope->filePath() + u")"_qs))
: m_scope->internalName())
+ u"::"_qs;
};
@@ -67,6 +67,10 @@ QString QQmlJSRegisterContent::descriptiveName() const
case ImportNamespace: {
return u"import namespace %1"_qs.arg(std::get<uint>(m_content));
}
+ case Conversion: {
+ return u"conversion to %1"_qs.arg(
+ std::get<ConvertedTypes>(m_content).result->internalName());
+ }
}
Q_UNREACHABLE();
return result + u"wat?"_qs;
@@ -83,6 +87,9 @@ bool QQmlJSRegisterContent::isList() const
return prop.isList()
|| prop.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
}
+ case Conversion:
+ return std::get<ConvertedTypes>(m_content).result->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Sequence;
default:
return false;
}
@@ -153,6 +160,17 @@ QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr
return result;
}
+QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr &storedType,
+ const QList<QQmlJSScope::ConstPtr> origins,
+ const QQmlJSScope::ConstPtr &conversion,
+ ContentVariant variant,
+ const QQmlJSScope::ConstPtr &scope)
+{
+ QQmlJSRegisterContent result(storedType, scope, variant);
+ result.m_content = ConvertedTypes { origins, conversion };
+ return result;
+}
+
bool operator==(const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
{
return a.m_storedType == b.m_storedType && a.m_variant == b.m_variant && a.m_scope == b.m_scope
diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h
index 174eeeb3bc..690ee5da4d 100644
--- a/src/qmlcompiler/qqmljsregistercontent_p.h
+++ b/src/qmlcompiler/qqmljsregistercontent_p.h
@@ -77,6 +77,9 @@ public:
ExtensionObjectMethod,
ExtensionObjectEnum,
+ MethodReturnValue,
+ JavaScriptReturnValue,
+
ListValue,
Builtin,
Unknown,
@@ -98,6 +101,7 @@ public:
bool isEnumeration() const { return m_content.index() == Enum; }
bool isMethod() const { return m_content.index() == Method; }
bool isImportNamespace() const { return m_content.index() == ImportNamespace; }
+ bool isConversion() const { return m_content.index() == Conversion; }
bool isList() const;
bool isWritable() const;
@@ -118,6 +122,16 @@ public:
QList<QQmlJSMetaMethod> method() const { return std::get<QList<QQmlJSMetaMethod>>(m_content); }
uint importNamespace() const { return std::get<uint>(m_content); }
+ QQmlJSScope::ConstPtr conversionResult() const
+ {
+ return std::get<ConvertedTypes>(m_content).result;
+ }
+
+ QList<QQmlJSScope::ConstPtr> conversionOrigins() const
+ {
+ return std::get<ConvertedTypes>(m_content).origins;
+ }
+
ContentVariant variant() const { return m_variant; }
friend size_t qHash(const QQmlJSRegisterContent &registerContent, size_t seed = 0)
@@ -136,6 +150,8 @@ public:
return qHash(std::get<QList<QQmlJSMetaMethod>>(registerContent.m_content), seed);
case ImportNamespace:
return qHash(std::get<uint>(registerContent.m_content), seed);
+ case Conversion:
+ return qHash(std::get<ConvertedTypes>(registerContent.m_content), seed);
}
Q_UNREACHABLE();
@@ -164,6 +180,12 @@ public:
uint importNamespaceStringId, ContentVariant variant,
const QQmlJSScope::ConstPtr &scope = {});
+ static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
+ const QList<QQmlJSScope::ConstPtr> origins,
+ const QQmlJSScope::ConstPtr &conversion,
+ ContentVariant variant,
+ const QQmlJSScope::ConstPtr &scope = {});
+
QQmlJSRegisterContent storedIn(const QQmlJSScope::ConstPtr &newStoredType) const
{
QQmlJSRegisterContent result = *this;
@@ -172,14 +194,36 @@ public:
}
private:
- enum ContentKind { Type, Property, Enum, Method, ImportNamespace };
+ enum ContentKind { Type, Property, Enum, Method, ImportNamespace, Conversion };
+
+ struct ConvertedTypes
+ {
+ QList<QQmlJSScope::ConstPtr> origins;
+ QQmlJSScope::ConstPtr result;
+
+ friend size_t qHash(const ConvertedTypes &types, size_t seed = 0)
+ {
+ return qHashMulti(seed, types.origins, types.result);
+ }
+
+ friend bool operator==(const ConvertedTypes &a, const ConvertedTypes &b)
+ {
+ return a.origins == b.origins && a.result == b.result;
+ }
+
+ friend bool operator!=(const ConvertedTypes &a, const ConvertedTypes &b)
+ {
+ return !(a == b);
+ }
+ };
using Content = std::variant<
QQmlJSScope::ConstPtr,
QQmlJSMetaProperty,
std::pair<QQmlJSMetaEnum, QString>,
QList<QQmlJSMetaMethod>,
- uint
+ uint,
+ ConvertedTypes
>;
QQmlJSRegisterContent(const QQmlJSScope::ConstPtr &storedType,
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index c01c5d9471..24f1ba6687 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -103,6 +103,15 @@ QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &par
return childScope;
}
+QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
+{
+ if (origin.isNull())
+ return QQmlJSScope::Ptr();
+ QQmlJSScope::Ptr cloned = create(origin->m_scopeType, origin->m_parentScope);
+ *cloned = *origin;
+ return cloned;
+}
+
void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
{
Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope);
@@ -437,11 +446,11 @@ void QQmlJSScope::resolveNonEnumTypes(
void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType)
{
- Q_ASSERT(intType); // There always has to be a builtin "int" type
for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end;
++it) {
if (it->type())
continue;
+ Q_ASSERT(intType); // We need an "int" type to resolve enums
auto enumScope = QQmlJSScope::create(EnumScope, self);
enumScope->m_baseTypeName = QStringLiteral("int");
enumScope->m_baseType.scope = intType;
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 2cc6555c30..72c57be7d6 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -59,7 +59,6 @@ class QQmlJSImporter;
class QQmlJSScope
{
- Q_DISABLE_COPY(QQmlJSScope)
public:
QQmlJSScope(QQmlJSScope &&) = default;
QQmlJSScope &operator=(QQmlJSScope &&) = default;
@@ -181,7 +180,8 @@ public:
};
static QQmlJSScope::Ptr create(ScopeType type = QQmlJSScope::QMLScope,
- const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin);
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
QQmlJSScope::Ptr parentScope()
@@ -224,8 +224,8 @@ public:
void setAnnotations(const QList<QQmlJSAnnotation> &annotation) { m_annotations = std::move(annotation); }
const QList<QQmlJSAnnotation> &annotations() const { return m_annotations; }
- QString fileName() const { return m_fileName; }
- void setFileName(const QString &file) { m_fileName = file; }
+ QString filePath() const { return m_filePath; }
+ void setFilePath(const QString &file) { m_filePath = file; }
// The name the type uses to refer to itself. Either C++ class name or base name of
// QML file. isComposite tells us if this is a C++ or a QML name.
@@ -462,6 +462,8 @@ public:
private:
QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ QQmlJSScope(const QQmlJSScope &) = default;
+ QQmlJSScope &operator=(const QQmlJSScope &) = default;
static ImportedScope<QQmlJSScope::ConstPtr> findType(
const QString &name, const ContextualTypes &contextualTypes,
@@ -484,7 +486,7 @@ private:
QVector<QQmlJSScope::Ptr> m_childScopes;
QQmlJSScope::WeakPtr m_parentScope;
- QString m_fileName;
+ QString m_filePath;
QString m_internalName;
QString m_baseTypeName;
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index e1b98deda0..283553f758 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -58,18 +58,22 @@ void QQmlJSShadowCheck::run(
m_annotations = annotations;
m_function = function;
m_error = error;
- m_state = initialState(function, m_typeResolver);
+ m_state = initialState(function);
decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
}
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->stringForIndex(nameIndex));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->stringForIndex(nameIndex));
}
void QQmlJSShadowCheck::generate_GetLookup(int index)
{
- checkShadowing(m_state.accumulatorIn, m_jsUnitGenerator->lookupName(index));
+ auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn != m_state.registers.end())
+ checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->lookupName(index));
}
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
@@ -122,7 +126,7 @@ void QQmlJSShadowCheck::checkShadowing(
}
setError(u"Member %1 of %2 can be shadowed"_qs
- .arg(memberName, m_state.accumulatorIn.descriptiveName()));
+ .arg(memberName, m_state.accumulatorIn().descriptiveName()));
return;
}
default:
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 4512d596e1..ce07a333d5 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -61,39 +61,25 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
}
}
- for (QQmlJSScope::ConstPtr &argument : function->argumentTypes) {
- Q_ASSERT(argument);
- if (QQmlJSScope::ConstPtr stored = m_typeResolver->genericType(
- argument, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- argument = std::move(stored);
- } else {
- setError(QStringLiteral("Cannot store the argument type %1.")
- .arg(argument->internalName(), 0));
- return InstructionAnnotations();
- }
- }
+ const auto transformRegister = [&](QQmlJSRegisterContent &content) {
+ if (const QQmlJSScope::ConstPtr &specific = content.storedType())
+ m_typeResolver->generalizeType(specific);
+ };
- const auto transformRegisters = [&](QHash<int, QQmlJSRegisterContent> &registers, int offset) {
- for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j) {
- const QQmlJSRegisterContent &content = *j;
- if (QQmlJSScope::ConstPtr specific = content.storedType()) {
- if (QQmlJSScope::ConstPtr generic = m_typeResolver->genericType(specific)) {
- *j = content.storedIn(generic);
- } else {
- setError(QStringLiteral("Cannot store the register type %1.")
- .arg(specific->internalName()), offset);
- return false;
- }
- }
- }
- return true;
+ const auto transformRegisters
+ = [&](QFlatMap<int, QQmlJSRegisterContent> &registers) {
+ for (auto j = registers.begin(), jEnd = registers.end(); j != jEnd; ++j)
+ transformRegister(j.value());
};
+ for (QQmlJSRegisterContent &argument : function->argumentTypes) {
+ Q_ASSERT(argument.isValid());
+ transformRegister(argument);
+ }
+
for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
- if (!transformRegisters(i->registers, i.key()))
- return InstructionAnnotations();
- if (!transformRegisters(i->expectedTargetTypesBeforeJump, i.key()))
- return InstructionAnnotations();
+ transformRegister(i->second.changedRegister);
+ transformRegisters(i->second.typeConversions);
}
return annotations;
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 63a552e293..440cf2da29 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -213,7 +213,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
} else if (script) {
QString name = toString(script->qualifiedId);
if (name == QLatin1String("file")) {
- scope->setFileName(readStringBinding(script));
+ scope->setFilePath(readStringBinding(script));
} else if (name == QLatin1String("name")) {
scope->setInternalName(readStringBinding(script));
} else if (name == QLatin1String("prototype")) {
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 6072393e0f..2760f3401b 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -60,8 +60,13 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_returnType = m_typeResolver->globalType(m_function->returnType);
do {
+ // Reset the error if we need to do another pass
+ if (m_state.needsMorePasses)
+ *m_error = QQmlJS::DiagnosticMessage();
+
+ m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
- m_state.State::operator=(initialState(m_function, m_typeResolver));
+ m_state.State::operator=(initialState(m_function));
reset();
decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
@@ -69,7 +74,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
// If we have found unresolved backwards jumps, we need to start over with a fresh state.
// Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case.
// This means that we won't start over for the same reason again.
- } while (!m_error->isValid() && m_state.needsMorePasses);
+ } while (m_state.needsMorePasses);
return m_state.annotations;
}
@@ -80,34 +85,41 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
return;
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
- m_logger->logWarning( \
- u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
- Log_Compiler); \
+ m_logger->log(u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
+ Log_Compiler, QQmlJS::SourceLocation()); \
return;
void QQmlJSTypePropagator::generate_Ret()
{
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
- } else if (!m_returnType.isValid() && m_state.accumulatorIn.isValid()
- && m_typeResolver->containedType(m_state.accumulatorIn)
- != m_typeResolver->voidType()) {
+ } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
+ && !m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->voidType())) {
setError(u"function without type annotation returns %1"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
- } else if (m_state.accumulatorIn != m_returnType
- && !canConvertFromTo(m_state.accumulatorIn, m_returnType)) {
+ } else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(),
+ .arg(m_state.accumulatorIn().descriptiveName(),
m_returnType.descriptiveName()));
- m_logger->logWarning(u"Cannot assign binding of type %1 to %2"_qs.arg(
- m_typeResolver->containedTypeName(m_state.accumulatorIn),
- m_typeResolver->containedTypeName(m_returnType)),
- Log_Type, getCurrentBindingSourceLocation());
+ m_logger->log(u"Cannot assign binding of type %1 to %2"_qs.arg(
+ m_typeResolver->containedTypeName(m_state.accumulatorIn()),
+ m_typeResolver->containedTypeName(m_returnType)),
+ Log_Type, getCurrentBindingSourceLocation());
return;
}
+ if (m_returnType.isValid()) {
+ // We need to preserve any possible undefined value as that resets the property.
+ if (m_typeResolver->canHoldUndefined(m_state.accumulatorIn()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_returnType);
+ }
+
+ m_state.setHasSideEffects(true);
m_state.skipInstructionsUntilNextJumpTarget = true;
}
@@ -119,37 +131,37 @@ void QQmlJSTypePropagator::generate_Debug()
void QQmlJSTypePropagator::generate_LoadConst(int index)
{
auto encodedConst = m_jsUnitGenerator->constant(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadZero()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadFalse()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->nullType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
}
void QQmlJSTypePropagator::generate_LoadUndefined()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->voidType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
}
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
@@ -160,17 +172,24 @@ void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
void QQmlJSTypePropagator::generate_LoadReg(int reg)
{
- m_state.accumulatorOut = checkedInputRegister(reg);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(Accumulator, checkedInputRegister(reg));
}
void QQmlJSTypePropagator::generate_StoreReg(int reg)
{
- setRegister(reg, m_state.accumulatorIn);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(reg, m_state.accumulatorIn());
}
void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
{
- setRegister(destReg, m_state.registers[srcReg]);
+ Q_ASSERT(destReg != InvalidRegister);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(destReg, m_state.registers[srcReg]);
}
void QQmlJSTypePropagator::generate_LoadImport(int index)
@@ -182,7 +201,7 @@ void QQmlJSTypePropagator::generate_LoadImport(int index)
void QQmlJSTypePropagator::generate_LoadLocal(int index)
{
Q_UNUSED(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_StoreLocal(int index)
@@ -208,7 +227,7 @@ void QQmlJSTypePropagator::generate_StoreScopedLocal(int scope, int index)
void QQmlJSTypePropagator::generate_LoadRuntimeString(int stringId)
{
Q_UNUSED(stringId)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
// m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
}
@@ -228,8 +247,8 @@ void QQmlJSTypePropagator::generate_LoadClosure(int value)
void QQmlJSTypePropagator::generate_LoadName(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(m_function->qmlScope, name);
- if (!m_state.accumulatorOut.isValid())
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -260,7 +279,7 @@ QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() c
return combine(entries.constFirst().location, entries.constLast().location);
}
-void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
+void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isMethod) const
{
auto location = getCurrentSourceLocation();
@@ -271,6 +290,13 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
return;
}
+ if (isMethod) {
+ if (isCallingProperty(m_function->qmlScope, name))
+ return;
+ } else if (isMissingPropertyType(m_function->qmlScope, name)) {
+ return;
+ }
+
std::optional<FixSuggestion> suggestion;
auto childScopes = m_function->qmlScope->childScopes();
@@ -297,7 +323,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
const auto handler = m_typeResolver->signalHandlers()[id.location];
- QString fixString = handler.isMultiline ? u" function("_qs : u" ("_qs;
+ QString fixString = handler.isMultiline ? u"function("_qs : u"("_qs;
const auto parameters = handler.signalParameters;
for (int numParams = parameters.size(); numParams > 0; --numParams) {
fixString += parameters.at(parameters.size() - numParams);
@@ -314,7 +340,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
"function instead.\n")
.arg(id.location.startLine)
.arg(id.location.startColumn),
- fixLocation, fixString
+ fixLocation, fixString, QString(), false
};
}
break;
@@ -334,7 +360,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
name + QLatin1String(" is a member of a parent element\n")
+ QLatin1String(" You can qualify the access with its id "
"to avoid this warning:\n"),
- fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.'))
+ fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.')), QString(), id.isEmpty()
};
if (id.isEmpty()) {
@@ -358,8 +384,8 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
}
}
- m_logger->logWarning(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true,
- true, suggestion);
+ m_logger->log(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true, true,
+ suggestion);
}
void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name,
@@ -407,65 +433,122 @@ void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QS
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, getCurrentSourceLocation());
+ m_logger->log(message, Log_Deprecation, getCurrentSourceLocation());
}
-bool QQmlJSTypePropagator::checkRestricted(const QString &propertyName) const
+bool QQmlJSTypePropagator::isRestricted(const QString &propertyName) const
{
QString restrictedKind;
- if (!m_state.accumulatorIn.isValid())
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn == m_state.registers.end())
return false;
- if (m_state.accumulatorIn.isList() && propertyName != u"length") {
+ if (accumulatorIn.value().isList() && propertyName != u"length") {
restrictedKind = u"a list"_qs;
- } else if (m_state.accumulatorIn.isEnumeration()
- && !m_state.accumulatorIn.enumeration().hasKey(propertyName)) {
+ } else if (accumulatorIn.value().isEnumeration()
+ && !accumulatorIn.value().enumeration().hasKey(propertyName)) {
restrictedKind = u"an enum"_qs;
- } else if (m_state.accumulatorIn.isMethod()) {
+ } else if (accumulatorIn.value().isMethod()) {
restrictedKind = u"a method"_qs;
}
if (!restrictedKind.isEmpty())
- m_logger->logWarning(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(
- restrictedKind, propertyName),
- Log_Type, getCurrentSourceLocation());
+ m_logger->log(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(restrictedKind,
+ propertyName),
+ Log_Type, getCurrentSourceLocation());
return !restrictedKind.isEmpty();
}
+// Only to be called once a lookup has already failed
+bool QQmlJSTypePropagator::isMissingPropertyType(QQmlJSScope::ConstPtr scope,
+ const QString &propertyName) const
+{
+ auto property = scope->property(propertyName);
+ if (!property.isValid())
+ return false;
+
+ QString errorType;
+ if (property.type().isNull())
+ errorType = u"found"_qs;
+ else if (!property.type()->isFullyResolved())
+ errorType = u"fully resolved"_qs;
+
+ Q_ASSERT(!errorType.isEmpty());
+
+ m_logger->log(
+ u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
+ .arg(property.typeName(), propertyName, errorType),
+ Log_Type, getCurrentSourceLocation());
+
+ return true;
+}
+
+bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const
+{
+ auto property = scope->property(name);
+ if (!property.isValid())
+ return false;
+
+ QString propertyType = u"Property"_qs;
+
+ auto methods = scope->methods(name);
+
+ QString errorType;
+ if (!methods.isEmpty()) {
+ errorType = u"shadowed by a property."_qs;
+ switch (methods.first().methodType()) {
+ case QQmlJSMetaMethod::Signal:
+ propertyType = u"Signal"_qs;
+ break;
+ case QQmlJSMetaMethod::Slot:
+ propertyType = u"Slot"_qs;
+ break;
+ case QQmlJSMetaMethod::Method:
+ propertyType = u"Method"_qs;
+ break;
+ }
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
+ errorType =
+ u"a variant property. It may or may not be a method. Use a regular function instead."_qs;
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->jsValueType())) {
+ errorType =
+ u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_qs;
+ } else {
+ errorType = u"not a method"_qs;
+ }
+
+ m_logger->log(u"%1 \"%2\" is %3"_qs.arg(propertyType, name, errorType), Log_Type,
+ getCurrentSourceLocation(), true, true, {});
+
+ return true;
+}
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
+ // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
+ // Any import namespaces etc. are handled via LoadProperty or GetLookup.
+
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(
- m_function->qmlScope,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
- + u'.' + name
- : name);
-
- if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
- const QQmlJSRegisterContent inType = m_state.accumulatorIn.isValid()
- ? m_state.accumulatorIn
- : m_typeResolver->globalType(m_function->qmlScope);
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
- m_typeResolver->containedType(inType));
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+
+ if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
+ const QQmlJSRegisterContent inType = m_typeResolver->globalType(m_function->qmlScope);
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_typeResolver->voidType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
+ m_typeResolver->containedType(inType)));
return;
}
checkDeprecated(m_function->qmlScope, name, false);
- bool isRestricted = checkRestricted(name);
-
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot access value for name "_qs + name);
-
- if (!isRestricted)
- handleUnqualifiedAccess(name);
- } else if (m_typeResolver->genericType(m_state.accumulatorOut.storedType()).isNull()) {
+ handleUnqualifiedAccess(name, false);
+ } else if (m_typeResolver->genericType(m_state.accumulatorOut().storedType()).isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
setError(u"Cannot determine generic type for "_qs + name);
@@ -490,20 +573,24 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
if (!type.isWritable() && !m_function->qmlScope->hasOwnProperty(name)) {
setError(u"Can't assign to read-only property %1"_qs.arg(name));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
- getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, type)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), type)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), type.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), type.descriptiveName()));
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
@@ -511,41 +598,76 @@ void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
void QQmlJSTypePropagator::generate_LoadElement(int base)
{
const QQmlJSRegisterContent baseRegister = m_state.registers[base];
- if (!m_typeResolver->registerContains(m_state.accumulatorIn, m_typeResolver->intType())
- || baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ setAccumulator(jsValue);
return;
}
- m_state.accumulatorOut = m_typeResolver->valueType(baseRegister);
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->intType()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ setAccumulator(m_typeResolver->valueType(baseRegister));
}
void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
{
- Q_UNUSED(base)
- Q_UNUSED(index)
+ const QQmlJSRegisterContent baseRegister = m_state.registers[base];
+ const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(indexRegister)) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ addReadRegister(index, jsValue);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(indexRegister, m_typeResolver->intType()))
+ addReadRegister(index, indexRegister);
+ else
+ addReadRegister(index, m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ addReadAccumulator(m_typeResolver->valueType(baseRegister));
+
+ // If we're writing a QQmlListProperty backed by a container somewhere else,
+ // that has side effects.
+ // If we're writing to a list retrieved from a property, that _should_ have side effects,
+ // but currently the QML engine doesn't implement them.
+ // TODO: Figure out the above and accurately set the flag.
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{
- m_state.accumulatorOut =
+ setAccumulator(
m_typeResolver->memberType(
- m_state.accumulatorIn,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
+ m_state.accumulatorIn(),
+ m_state.accumulatorIn().isImportNamespace()
+ ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
+ u'.' + propertyName
- : propertyName);
+ : propertyName));
if (m_typeInfo != nullptr
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ScopeAttached) {
- QQmlJSScope::ConstPtr attachedType = m_state.accumulatorIn.scopeType();
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ScopeAttached) {
+ QQmlJSScope::ConstPtr attachedType = m_typeResolver->originalType(
+ m_state.accumulatorIn().scopeType());
for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
scope = scope->parentScope()) {
if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
// Ignore enum accesses, as these will not cause the attached object to be created
- if (m_state.accumulatorOut.isValid() && m_state.accumulatorOut.isEnumeration())
+ if (m_state.accumulatorOut().isValid() && m_state.accumulatorOut().isEnumeration())
continue;
const QString id = m_function->addressableScopes.id(scope);
@@ -555,11 +677,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
fixLocation.length = 0;
- suggestion.fixes << FixSuggestion::Fix {
- u"Reference it by id instead:"_qs,
- fixLocation,
- id.isEmpty() ? u"<id>."_qs : (id + u'.')
- };
+ suggestion.fixes << FixSuggestion::Fix { u"Reference it by id instead:"_qs,
+ fixLocation,
+ id.isEmpty() ? u"<id>."_qs : (id + u'.'),
+ QString(), id.isEmpty() };
fixLocation = scope->sourceLocation();
fixLocation.length = 0;
@@ -571,9 +692,9 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{} };
}
- m_logger->logWarning(
+ m_logger->log(
u"Using attached type %1 already initialized in a parent scope."_qs.arg(
- m_state.accumulatorIn.scopeType()->internalName()),
+ m_state.accumulatorIn().scopeType()->internalName()),
Log_AttachedPropertyReuse, getCurrentSourceLocation(), true, true,
suggestion);
}
@@ -581,61 +702,47 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
}
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
- Q_ASSERT(m_state.accumulatorIn.isValid());
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- m_state.accumulatorIn.storedType(),
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ addReadAccumulator(m_state.accumulatorIn());
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_state.accumulatorIn().storedType(),
m_jsUnitGenerator->getStringId(propertyName),
QQmlJSRegisterContent::ObjectModulePrefix,
- m_typeResolver->containedType(m_state.accumulatorIn));
+ m_typeResolver->containedType(m_state.accumulatorIn())));
return;
}
- if (m_state.accumulatorIn.isImportNamespace())
- m_logger->logWarning(u"Type not found in namespace"_qs, Log_Type,
- getCurrentSourceLocation());
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
- m_logger->logWarning(u"Cannot load singleton as property of object"_qs, Log_Type,
- getCurrentSourceLocation());
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ if (m_state.accumulatorIn().isImportNamespace())
+ m_logger->log(u"Type not found in namespace"_qs, Log_Type, getCurrentSourceLocation());
+ } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
+ m_logger->log(u"Cannot load singleton as property of object"_qs, Log_Type,
+ getCurrentSourceLocation());
+ setAccumulator(QQmlJSRegisterContent());
}
- bool isRestricted = checkRestricted(propertyName);
+ const bool isRestrictedProperty = isRestricted(propertyName);
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot load property %1 from %2."_qs
- .arg(propertyName, m_state.accumulatorIn.descriptiveName()));
+ .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
- if (isRestricted)
+ if (isRestrictedProperty)
return;
- const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn);
+ const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn());
if (typeName == u"QVariant")
return;
- if (m_state.accumulatorIn.isList() && propertyName == u"length")
+ if (m_state.accumulatorIn().isList() && propertyName == u"length")
return;
- auto baseType = m_typeResolver->containedType(m_state.accumulatorIn);
+ auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
// Warn separately when a property is only not found because of a missing type
- if (auto property = baseType->property(propertyName); property.isValid()) {
-
- QString errorType;
- if (property.type().isNull())
- errorType = u"found"_qs;
- else if (!property.type()->isFullyResolved())
- errorType = u"fully resolved"_qs;
-
- Q_ASSERT(!errorType.isEmpty());
-
- m_logger->logWarning(
- u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
- .arg(property.typeName(), propertyName, errorType),
- Log_Type, getCurrentSourceLocation());
+ if (isMissingPropertyType(baseType, propertyName))
return;
- }
std::optional<FixSuggestion> fixSuggestion;
@@ -650,30 +757,45 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
}
- m_logger->logWarning(
+ m_logger->log(
u"Property \"%1\" not found on type \"%2\""_qs.arg(propertyName).arg(typeName),
Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- if (m_state.accumulatorOut.isMethod() && m_state.accumulatorOut.method().length() != 1) {
+ if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().length() != 1) {
setError(u"Cannot determine overloaded method on loadProperty"_qs);
return;
}
- if (m_state.accumulatorOut.isProperty()) {
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType())) {
+ if (m_state.accumulatorOut().isProperty()) {
+ if (m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
setError(u"Type %1 does not have a property %2 for reading"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), propertyName));
+ .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
return;
}
- if (!m_state.accumulatorOut.property().type()) {
- m_logger->logWarning(
- QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
- Log_Type, getCurrentSourceLocation());
+ if (!m_state.accumulatorOut().property().type()) {
+ m_logger->log(
+ QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
+ Log_Type, getCurrentSourceLocation());
}
}
+
+ switch (m_state.accumulatorOut().variant()) {
+ case QQmlJSRegisterContent::ObjectEnum:
+ case QQmlJSRegisterContent::ExtensionObjectEnum:
+ case QQmlJSRegisterContent::Singleton:
+ // For reading enums or singletons, we don't need to access anything, unless it's an
+ // import namespace. Then we need the name.
+ if (m_state.accumulatorIn().isImportNamespace())
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ default:
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ }
}
void QQmlJSTypePropagator::generate_LoadProperty(int nameIndex)
@@ -715,17 +837,21 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
if (!property.isWritable()) {
setError(u"Can't assign to read-only property %1"_qs.arg(propertyName));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(propertyName),
- Log_Property, getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(propertyName), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, property)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), property.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
return;
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(property);
+ addReadRegister(base, callBase);
}
void QQmlJSTypePropagator::generate_SetLookup(int index, int base)
@@ -762,6 +888,7 @@ void QQmlJSTypePropagator::generate_Resume(int)
void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -770,6 +897,7 @@ void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -779,22 +907,47 @@ void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, i
void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
{
- auto callBase = m_state.registers[base];
+ Q_ASSERT(m_state.registers.contains(base));
+ const auto callBase = m_state.registers[base];
const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
- const auto member = m_typeResolver->memberType(callBase, propertyName);
- const auto containedType = m_typeResolver->containedType(callBase);
- if (containedType == m_typeResolver->jsValueType()
- || containedType == m_typeResolver->varType()) {
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ if (m_typeResolver->registerContains(
+ callBase, m_typeResolver->jsGlobalObject()->property(u"Math"_qs).type())) {
+
+ // If we call a method on the Math object we don't need the actual Math object. We do need
+ // to transfer the type information to the code generator so that it knows that this is the
+ // Math object. Read the base register as void. void isn't stored, and the place where it's
+ // created will be optimized out if there are no other readers. The code generator can
+ // retrieve the original type and determine that it was the Math object.
+ addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
+
+ QQmlJSRegisterContent realType = m_typeResolver->globalType(m_typeResolver->realType());
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, realType);
+ setAccumulator(realType);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
+ || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
+ const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(base, jsValueType);
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, jsValueType);
+ setAccumulator(jsValueType);
+ m_state.setHasSideEffects(true);
return;
}
+ const auto member = m_typeResolver->memberType(callBase, propertyName);
if (!member.isMethod()) {
setError(u"Type %1 does not have a property %2 for calling"_qs
.arg(callBase.descriptiveName(), propertyName));
- if (checkRestricted(propertyName))
+ if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
+ return;
+
+ if (isRestricted(propertyName))
return;
std::optional<FixSuggestion> fixSuggestion;
@@ -809,14 +962,15 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
}
}
- m_logger->logWarning(u"Property \"%1\" not found on type \"%2\""_qs.arg(
- propertyName, m_typeResolver->containedTypeName(callBase)),
- Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
+ m_logger->log(u"Property \"%1\" not found on type \"%2\""_qs.arg(
+ propertyName, m_typeResolver->containedTypeName(callBase)),
+ Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- checkDeprecated(containedType, propertyName, true);
+ checkDeprecated(m_typeResolver->containedType(callBase), propertyName, true);
+ addReadRegister(base, callBase);
propagateCall(member.method(), argc, argv);
}
@@ -873,6 +1027,72 @@ QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMe
return javascriptFunction;
}
+void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
+{
+ setRegister(Accumulator, content);
+}
+
+void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
+{
+ // If we've come to the same conclusion before, let's not track the type again.
+ auto it = m_prevStateAnnotations.find(currentInstructionOffset());
+ if (it != m_prevStateAnnotations.end()) {
+ const QQmlJSRegisterContent &lastTry = it->second.changedRegister;
+ if (m_typeResolver->registerContains(lastTry, m_typeResolver->containedType(content))) {
+ m_state.setRegister(index, lastTry);
+ return;
+ }
+ }
+
+ m_state.setRegister(index, m_typeResolver->tracked(content));
+}
+
+void QQmlJSTypePropagator::mergeRegister(
+ int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
+{
+ auto merged = m_typeResolver->merge(a, b);
+
+ Q_ASSERT(merged.isValid());
+ Q_ASSERT(merged.isConversion());
+
+ auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
+ auto it = m_prevStateAnnotations.find(currentInstructionOffset());
+ if (it == m_prevStateAnnotations.end())
+ return false;
+
+ auto conversion = it->second.typeConversions.find(index);
+ if (conversion == it->second.typeConversions.end())
+ return false;
+
+ const QQmlJSRegisterContent &lastTry = conversion.value();
+
+ Q_ASSERT(lastTry.isValid());
+ Q_ASSERT(lastTry.isConversion());
+
+ if (!m_typeResolver->equals(lastTry.conversionResult(), merged.conversionResult())
+ || lastTry.conversionOrigins() != merged.conversionOrigins()) {
+ return false;
+ }
+
+ // We don't need to track it again if we've come to the same conclusion before.
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
+ m_state.registers[index] = lastTry;
+ return true;
+ };
+
+ if (!tryPrevStateConversion(index, merged)) {
+ merged = m_typeResolver->tracked(merged);
+ Q_ASSERT(merged.isValid());
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
+ m_state.registers[index] = merged;
+ }
+}
+
+void QQmlJSTypePropagator::addReadRegister(int index, const QQmlJSRegisterContent &convertTo)
+{
+ m_state.addReadRegister(index, m_typeResolver->convert(m_state.registers[index], convertTo));
+}
+
void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
{
QStringList errors;
@@ -887,11 +1107,30 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods,
return;
}
- const auto returnType = match.returnType();
- m_state.accumulatorOut = m_typeResolver->globalType(
- returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType());
- if (!m_state.accumulatorOut.isValid())
+ const auto returnType = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(match.returnType());
+ setAccumulator(m_typeResolver->returnType(
+ returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType(),
+ match.isJavaScriptFunction() ? QQmlJSRegisterContent::JavaScriptReturnValue
+ : QQmlJSRegisterContent::MethodReturnValue));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_qs.arg(match.methodName()));
+
+ m_state.setHasSideEffects(true);
+ const auto types = match.parameterTypes();
+ for (int i = 0; i < argc; ++i) {
+ if (i < types.length()) {
+ const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(types.at(i));
+ if (!type.isNull()) {
+ addReadRegister(argv + i, m_typeResolver->globalType(type));
+ continue;
+ }
+ }
+ addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ }
}
void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base, int argc,
@@ -902,6 +1141,7 @@ void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base
void QQmlJSTypePropagator::generate_CallElement(int base, int index, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(base)
Q_UNUSED(index)
Q_UNUSED(argc)
@@ -916,6 +1156,7 @@ void QQmlJSTypePropagator::generate_CallName(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(argc)
Q_UNUSED(argv)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -934,10 +1175,11 @@ void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName,
}
setError(u"method %1 cannot be resolved."_qs.arg(functionName));
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
setError(u"Cannot find function '%1'"_qs.arg(functionName));
- handleUnqualifiedAccess(functionName);
+
+ handleUnqualifiedAccess(functionName, true);
}
void QQmlJSTypePropagator::generate_CallGlobalLookup(int index, int argc, int argv)
@@ -954,6 +1196,7 @@ void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(int index, int
void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -963,6 +1206,7 @@ void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int
void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -972,16 +1216,18 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argv)
Q_UNUSED(argc)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -990,17 +1236,20 @@ void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int
void QQmlJSTypePropagator::generate_SetUnwindHandler(int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindDispatch()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(level)
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -1014,7 +1263,9 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ setAccumulator(QQmlJSRegisterContent());
+ m_state.setHasSideEffects(true);
+ m_state.skipInstructionsUntilNextJumpTarget = true;
}
void QQmlJSTypePropagator::generate_GetException()
@@ -1024,16 +1275,18 @@ void QQmlJSTypePropagator::generate_GetException()
void QQmlJSTypePropagator::generate_SetException()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CreateCallContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
@@ -1041,34 +1294,39 @@ void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
void QQmlJSTypePropagator::generate_PushWithContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushBlockContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CloneBlockContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushScriptContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopScriptContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
@@ -1118,12 +1376,12 @@ void QQmlJSTypePropagator::generate_DeleteName(int name)
void QQmlJSTypePropagator::generate_TypeofName(int name)
{
Q_UNUSED(name);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_TypeofValue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
@@ -1135,9 +1393,10 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(argc == 0
+ ? m_typeResolver->emptyListType()
+ : m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1147,7 +1406,7 @@ void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int
Q_UNUSED(internalClassId)
Q_UNUSED(argc)
Q_UNUSED(args)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1192,36 +1451,40 @@ void QQmlJSTypePropagator::generate_ToObject()
void QQmlJSTypePropagator::generate_Jump(int offset)
{
saveRegisterStateForJump(offset);
- m_state.accumulatorIn = QQmlJSRegisterContent();
- m_state.accumulatorOut = QQmlJSRegisterContent();
m_state.skipInstructionsUntilNextJumpTarget = true;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpTrue(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpFalse(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpNoException(int offset)
{
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
@@ -1232,77 +1495,174 @@ void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
void QQmlJSTypePropagator::generate_CheckException()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
+}
+
+void QQmlJSTypePropagator::recordEqualsNullType()
+{
+ // TODO: We can specialize this further, for QVariant, QJSValue, int, bool, whatever.
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->nullType())
+ || m_typeResolver->containedType(m_state.accumulatorIn())->isReferenceType()) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsIntType()
+{
+ // We have specializations for numeric types and bool.
+ const QQmlJSScope::ConstPtr in = m_typeResolver->containedType(m_state.accumulatorIn());
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->boolType())
+ || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsType(int lhs)
+{
+ const auto isNumericOrEnum = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration() || m_typeResolver->isNumeric(content);
+ };
+
+ const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration()
+ || m_typeResolver->registerContains(content, m_typeResolver->intType());
+ };
+
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const auto lhsRegister = m_state.registers[lhs];
+
+ // If the types are primitive, we compare directly ...
+ if (m_typeResolver->isPrimitive(accumulatorIn)) {
+ if (m_typeResolver->registerContains(
+ accumulatorIn, m_typeResolver->containedType(lhsRegister))) {
+ addReadRegister(lhs, accumulatorIn);
+ addReadAccumulator(accumulatorIn);
+ return;
+ } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) {
+ const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister)
+ ? m_typeResolver->globalType(m_typeResolver->intType())
+ : m_typeResolver->globalType(m_typeResolver->realType());
+ addReadRegister(lhs, targetType);
+ addReadAccumulator(targetType);
+ return;
+ } else if (m_typeResolver->isPrimitive(lhsRegister)) {
+ const QQmlJSRegisterContent primitive = m_typeResolver->globalType(
+ m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, primitive);
+ addReadAccumulator(primitive);
+ }
+ }
+
+ // Otherwise they're both casted to QJSValue.
+ // TODO: We can add more specializations here: void/void null/null object/null etc
+
+ const QQmlJSRegisterContent jsval = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(lhs, jsval);
+ addReadAccumulator(jsval);
+}
+
+void QQmlJSTypePropagator::recordCompareType(int lhs)
+{
+ // If they're both numeric, we can compare them directly.
+ // They may be casted to double, though.
+ const QQmlJSRegisterContent read
+ = (m_typeResolver->isNumeric(m_state.accumulatorIn())
+ && m_typeResolver->isNumeric(m_state.registers[lhs]))
+ ? m_typeResolver->merge(m_state.accumulatorIn(), m_state.registers[lhs])
+ : m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, read);
+ addReadAccumulator(read);
}
void QQmlJSTypePropagator::generate_CmpEqNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpNeNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpEq(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::Equal, lhs);
}
void QQmlJSTypePropagator::generate_CmpNe(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpGt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Gt, lhs);
}
void QQmlJSTypePropagator::generate_CmpGe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Ge, lhs);
}
void QQmlJSTypePropagator::generate_CmpLt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Lt, lhs);
}
void QQmlJSTypePropagator::generate_CmpLe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Le, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictNotEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpIn(int lhs)
{
+ // TODO: Most of the time we don't need the object at all, but only its metatype.
+ // Fix this when we add support for the "in" instruction to the code generator.
+ // Also, specialize on lhs to avoid conversion to QJSPrimitiveValue.
+
+ addReadRegister(lhs, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+
propagateBinaryOperation(QSOperator::Op::In, lhs);
}
@@ -1317,47 +1677,59 @@ void QQmlJSTypePropagator::generate_As(int lhs)
const QQmlJSRegisterContent input = checkedInputRegister(lhs);
QQmlJSScope::ConstPtr contained;
- switch (m_state.accumulatorIn.variant()) {
+ switch (m_state.accumulatorIn().variant()) {
case QQmlJSRegisterContent::ScopeAttached:
+ contained = m_state.accumulatorIn().scopeType();
+ break;
case QQmlJSRegisterContent::MetaType:
- contained = m_state.accumulatorIn.scopeType();
+ contained = m_state.accumulatorIn().scopeType();
+ if (contained->isComposite()) // Otherwise we don't need it
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
break;
default:
- contained = m_typeResolver->containedType(m_state.accumulatorIn);
+ contained = m_typeResolver->containedType(m_state.accumulatorIn());
break;
}
+ addReadRegister(lhs, m_typeResolver->globalType(contained));
+
if (m_typeResolver->containedType(input)->accessSemantics()
!= QQmlJSScope::AccessSemantics::Reference
|| contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
setError(u"invalid cast from %1 to %2. You can only cast object types."_qs
- .arg(input.descriptiveName(), m_state.accumulatorIn.descriptiveName()));
+ .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
} else {
- m_state.accumulatorOut = m_typeResolver->globalType(contained);
+ setAccumulator(m_typeResolver->globalType(contained));
}
}
void QQmlJSTypePropagator::generate_UNot()
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ const QQmlJSRegisterContent boolType = m_typeResolver->globalType(m_typeResolver->boolType());
+ addReadAccumulator(boolType);
+ setAccumulator(boolType);
}
void QQmlJSTypePropagator::generate_UPlus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UMinus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UCompl()
@@ -1367,19 +1739,25 @@ void QQmlJSTypePropagator::generate_UCompl()
void QQmlJSTypePropagator::generate_Increment()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Decrement()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Add(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAnd(int lhs)
@@ -1409,15 +1787,21 @@ void QQmlJSTypePropagator::generate_UShr(int lhs)
void QQmlJSTypePropagator::generate_Shr(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Shl(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAndConst(int rhs)
@@ -1448,18 +1832,22 @@ void QQmlJSTypePropagator::generate_ShrConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_ShlConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Exp(int lhs)
@@ -1470,22 +1858,30 @@ void QQmlJSTypePropagator::generate_Exp(int lhs)
void QQmlJSTypePropagator::generate_Mul(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Div(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Mod(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Sub(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
@@ -1506,45 +1902,53 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
QV4::Moth::ByteCodeHandler::Verdict
-QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
+QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
{
if (m_error->isValid())
return SkipInstruction;
- if (m_state.jumpTargets.contains(currentInstructionOffset()))
- m_state.skipInstructionsUntilNextJumpTarget = false;
- else if (m_state.skipInstructionsUntilNextJumpTarget)
+ if (m_state.jumpTargets.contains(currentInstructionOffset())) {
+ if (m_state.skipInstructionsUntilNextJumpTarget) {
+ // When re-surfacing from dead code, all registers are invalid.
+ m_state.registers.clear();
+ m_state.skipInstructionsUntilNextJumpTarget = false;
+ }
+ } else if (m_state.skipInstructionsUntilNextJumpTarget
+ && !instructionManipulatesContext(type)) {
return SkipInstruction;
-
- bool instructionWritesAccumulatorWithoutReading = false;
- switch (instr) {
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadZero:
- case QV4::Moth::Instr::Type::LoadTrue:
- case QV4::Moth::Instr::Type::LoadFalse:
- case QV4::Moth::Instr::Type::LoadConst:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadUndefined:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadLocal:
- case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::LoadGlobalLookup:
- case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::CallGlobalLookup:
- case QV4::Moth::Instr::Type::CallPropertyLookup:
- instructionWritesAccumulatorWithoutReading = true;
- break;
- default:
- break;
}
const int currentOffset = currentInstructionOffset();
// If we reach an instruction that is a target of a jump earlier, then we must check that the
// register state at the origin matches the current state. If not, then we may have to inject
- // conversion code (communicated to code gen via m_state.expectedTargetTypesBeforeJump). For
+ // conversion code (communicated to code gen via m_state.typeConversions). For
// example:
//
// function blah(x: number) { return x > 10 ? 10 : x}
@@ -1563,11 +1967,6 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
registerIt != end; ++registerIt) {
const int registerIndex = registerIt.key();
- // AccumulatorOut will be the "in" for this (upcoming) instruction, so if that one
- // merely writes to it, we don't care about it's value at the origin of the jump.
- if (registerIndex == Accumulator && instructionWritesAccumulatorWithoutReading)
- continue;
-
auto newType = registerIt.value();
if (!newType.isValid()) {
setError(u"When reached from offset %1, %2 is undefined"_qs
@@ -1578,60 +1977,34 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
auto currentRegister = m_state.registers.find(registerIndex);
if (currentRegister != m_state.registers.end()) {
- // Careful with accessing the hash iterator later inside this block,
- // some operations may invalidate it.
- auto currentRegisterType = currentRegister;
-
- if (*currentRegisterType != newType) {
- auto merged = m_typeResolver->merge(newType, *currentRegisterType);
- Q_ASSERT(merged.isValid());
- m_state.annotations[currentInstructionOffset()]
- .expectedTargetTypesBeforeJump[registerIndex] = merged;
- setRegister(registerIndex, merged);
+ if (currentRegister.value() != newType) {
+ mergeRegister(registerIndex, newType, currentRegister.value());
} else {
// Clear the constant value as this from a jump that might be merging two
// different value
// currentRegister->m_state.value = {};
}
+ } else {
+ mergeRegister(registerIndex, newType, newType);
}
}
}
- // Most instructions require a valid accumulator as input
- switch (instr) {
- case QV4::Moth::Instr::Type::Jump:
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadNull:
- case QV4::Moth::Instr::Type::TypeofName:
- case QV4::Moth::Instr::Type::CallProperty:
- case QV4::Moth::Instr::Type::CallName:
- case QV4::Moth::Instr::Type::MoveReg:
- case QV4::Moth::Instr::Type::MoveConst:
- case QV4::Moth::Instr::Type::DefineArray:
- case QV4::Moth::Instr::Type::DefineObjectLiteral:
- case QV4::Moth::Instr::Type::CheckException:
- case QV4::Moth::Instr::Type::CreateCallContext:
- case QV4::Moth::Instr::Type::PopContext:
- case QV4::Moth::Instr::Type::JumpNoException:
- case QV4::Moth::Instr::Type::SetUnwindHandler:
- case QV4::Moth::Instr::Type::PushCatchContext:
- case QV4::Moth::Instr::Type::UnwindDispatch:
- break;
- default:
- if (instructionWritesAccumulatorWithoutReading)
- m_state.accumulatorIn = QQmlJSRegisterContent();
- else
- m_state.accumulatorIn = checkedInputRegister(Accumulator);
- }
-
return ProcessInstruction;
}
void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
{
+ InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
+ currentInstruction.changedRegister = m_state.changedRegister();
+ currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
+ currentInstruction.readRegisters = m_state.takeReadRegisters();
+ currentInstruction.hasSideEffects = m_state.hasSideEffects();
+ currentInstruction.isRename = m_state.isRename();
+ m_state.setHasSideEffects(false);
+ m_state.setIsRename(false);
+ m_state.setReadRegisters(VirtualRegisters());
+
switch (instr) {
// the following instructions are not expected to produce output in the accumulator
case QV4::Moth::Instr::Type::Ret:
@@ -1653,30 +2026,45 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::SetUnwindHandler:
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
+ if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
+ setError(u"Instruction is not expected to populate the accumulator"_qs);
+ return;
+ }
break;
default:
// If the instruction is expected to produce output, save it in the register set
// for the next instruction.
- if (m_state.accumulatorOut.isValid()) {
- setRegister(Accumulator, m_state.accumulatorOut);
- m_state.accumulatorOut = QQmlJSRegisterContent();
- } else if (!m_error->isValid()) {
+ if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
+ && !m_error->isValid()) {
setError(u"Instruction is expected to populate the accumulator"_qs);
return;
}
}
- m_state.annotations[currentInstructionOffset()].registers = m_state.registers;
+ if (m_state.changedRegisterIndex() != InvalidRegister) {
+ Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
+ m_state.registers[m_state.changedRegisterIndex()] = m_state.changedRegister();
+ m_state.clearChangedRegister();
+ }
}
-void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
+QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
if (!lhsRegister.isValid())
- return;
+ return QQmlJSRegisterContent();
- m_state.accumulatorOut =
- m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ op, lhsRegister, m_state.accumulatorIn());
+
+ setAccumulator(type);
+
+ // If we're dealing with QJSPrimitiveType, do not force premature conversion of the arguemnts
+ // to the target type. Such an operation can lose information.
+ if (type.storedType() == m_typeResolver->jsPrimitiveType())
+ return m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+
+ return type;
}
void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
@@ -1692,8 +2080,10 @@ void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
const auto registerStates =
m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
for (auto it = registerStates.first; it != registerStates.second; ++it) {
- if (it->registers == state.registers)
+ if (it->registers.keys() == state.registers.keys()
+ && it->registers.values() == state.registers.values()) {
return; // We've seen the same register state before. No need for merging.
+ }
}
// The register state at the target offset needs to be resolved in a further pass.
@@ -1715,28 +2105,14 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const
registerIndex - FirstArgument - m_function->argumentTypes.count());
}
-// As the source register content may also be a register, we expect a copy here,
-// rather than a reference. Otherwise you might pass a reference to another entry
-// of m_state.registers, which then becomes invalid when making space for the new
-// entry in the hash.
-void QQmlJSTypePropagator::setRegister(int index, QQmlJSRegisterContent content)
-{
- m_state.registers[index] = std::move(content);
-}
-
-void QQmlJSTypePropagator::setRegister(int index, const QQmlJSScope::ConstPtr &content)
-{
- m_state.registers[index] = m_typeResolver->globalType(content);
-}
-
QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
{
- VirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
- if (regIt == m_state.registers.constEnd()) {
+ const auto regIt = m_state.registers.find(reg);
+ if (regIt == m_state.registers.end()) {
setError(u"Type error: could not infer the type of an expression"_qs);
return {};
}
- return *regIt;
+ return regIt.value();
}
bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index 1b3af43f60..8cc6879f09 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -208,13 +208,15 @@ private:
bool needsMorePasses = false;
};
- void handleUnqualifiedAccess(const QString &name) const;
+ void handleUnqualifiedAccess(const QString &name, bool isMethod) const;
void checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name, bool isMethod) const;
- bool checkRestricted(const QString &propertyName) const;
+ bool isRestricted(const QString &propertyName) const;
+ bool isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const;
+ bool isMissingPropertyType(QQmlJSScope::ConstPtr scope, const QString &type) const;
QQmlJS::SourceLocation getCurrentSourceLocation() const;
QQmlJS::SourceLocation getCurrentBindingSourceLocation() const;
- void propagateBinaryOperation(QSOperator::Op op, int lhs);
+ QQmlJSRegisterContent propagateBinaryOperation(QSOperator::Op op, int lhs);
void propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv);
void propagatePropertyLookup(const QString &name);
void propagateScopeLookupCall(const QString &functionName, int argc, int argv);
@@ -223,19 +225,32 @@ private:
QString registerName(int registerIndex) const;
- void setRegister(int index, QQmlJSRegisterContent content);
- void setRegister(int index, const QQmlJSScope::ConstPtr &content);
-
QQmlJSRegisterContent checkedInputRegister(int reg);
QQmlJSMetaMethod bestMatchForCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
QStringList *errors);
+ void setAccumulator(const QQmlJSRegisterContent &content);
+ void setRegister(int index, const QQmlJSRegisterContent &content);
+ void mergeRegister(int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b);
+
+ void addReadRegister(int index, const QQmlJSRegisterContent &convertTo);
+ void addReadAccumulator(const QQmlJSRegisterContent &convertTo)
+ {
+ addReadRegister(Accumulator, convertTo);
+ }
+
+ void recordEqualsNullType();
+ void recordEqualsIntType();
+ void recordEqualsType(int lhs);
+ void recordCompareType(int lhs);
+
QQmlJSRegisterContent m_returnType;
QQmlJSTypeInfo *m_typeInfo = nullptr;
// Not part of the state, as the back jumps are the reason for running multiple passes
QMultiHash<int, ExpectedRegisterState> m_jumpOriginRegisterStateByTargetInstructionOffset;
+ InstructionAnnotations m_prevStateAnnotations;
PassState m_state;
};
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 99de711ee6..1b0520e70d 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -64,6 +64,7 @@ static bool searchBaseAndExtensionTypes(const QQmlJSScope::ConstPtr type, const
}
QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
+ : m_typeTracker(std::make_unique<TypeTracker>())
{
const QQmlJSImporter::ImportedTypes builtinTypes = importer->builtinInternalNames();
m_voidType = builtinTypes[u"void"_qs].scope;
@@ -73,27 +74,33 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_intType = builtinTypes[u"int"_qs].scope;
m_boolType = builtinTypes[u"bool"_qs].scope;
m_stringType = builtinTypes[u"QString"_qs].scope;
+ m_stringListType = builtinTypes[u"QStringList"_qs].scope;
m_urlType = builtinTypes[u"QUrl"_qs].scope;
m_dateTimeType = builtinTypes[u"QDateTime"_qs].scope;
m_variantListType = builtinTypes[u"QVariantList"_qs].scope;
m_varType = builtinTypes[u"QVariant"_qs].scope;
m_jsValueType = builtinTypes[u"QJSValue"_qs].scope;
+ QQmlJSScope::Ptr emptyListType = QQmlJSScope::create();
+ emptyListType->setInternalName(u"void*"_qs);
+ emptyListType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
+ m_emptyListType = emptyListType;
+
QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_qs);
- jsPrimitiveType->setFileName(u"qjsprimitivevalue.h"_qs);
+ jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_qs);
jsPrimitiveType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
m_jsPrimitiveType = jsPrimitiveType;
QQmlJSScope::Ptr listPropertyType = QQmlJSScope::create();
listPropertyType->setInternalName(u"QQmlListProperty<QObject>"_qs);
- listPropertyType->setFileName(u"qqmllist.h"_qs);
+ listPropertyType->setFilePath(u"qqmllist.h"_qs);
listPropertyType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
m_listPropertyType = listPropertyType;
QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create();
metaObjectType->setInternalName(u"const QMetaObject"_qs);
- metaObjectType->setFileName(u"qmetaobject.h"_qs);
+ metaObjectType->setFilePath(u"qmetaobject.h"_qs);
metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
m_metaObjectType = metaObjectType;
@@ -137,19 +144,19 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
// If the property is defined in the same scope where it is set,
// we are in fact allowed to set it, even if it's not writable.
if (!property.isWritable() && !scope->hasOwnProperty(binding.propertyName())) {
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs
- .arg(binding.propertyName()),
- Log_Type, binding.sourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(
+ binding.propertyName()),
+ Log_Type, binding.sourceLocation());
continue;
}
- if (!canConvertFromTo(binding.literalType(), property.type())) {
- m_logger->logWarning(u"Cannot assign binding of type %1 to %2"_qs
- .arg(binding.literalTypeName())
- .arg(property.typeName()),
- Log_Type, binding.sourceLocation());
- } else if (property.type() == m_stringType && isNumeric(binding.literalType())) {
- m_logger->logWarning(u"Cannot assign a numeric constant to a string property"_qs,
- Log_Type, binding.sourceLocation());
+ if (!canConvertFromTo(binding.literalType(this), property.type())) {
+ m_logger->log(u"Cannot assign binding of type %1 to %2"_qs
+ .arg(binding.literalTypeName(), property.typeName()),
+ Log_Type, binding.sourceLocation());
+ } else if (equals(property.type(), m_stringType)
+ && isNumeric(binding.literalType(this))) {
+ m_logger->log(u"Cannot assign a numeric constant to a string property"_qs,
+ Log_Type, binding.sourceLocation());
}
}
}
@@ -170,6 +177,33 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
return m_objectsById.scope(id, referrer);
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &elementType) const
+{
+ auto it = m_typeTracker->listTypes.find(elementType);
+ if (it != m_typeTracker->listTypes.end())
+ return *it;
+
+ switch (elementType->accessSemantics()) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ return m_listPropertyType;
+ case QQmlJSScope::AccessSemantics::Value: {
+ QQmlJSScope::Ptr listType = QQmlJSScope::create();
+ listType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
+ listType->setValueTypeName(elementType->internalName());
+ listType->setInternalName(u"QList<%1>"_qs.arg(elementType->internalName()));
+ listType->setFilePath(elementType->filePath());
+ const QQmlJSImportedScope element = {elementType, QTypeRevision()};
+ QQmlJSScope::resolveTypes(listType, {{elementType->internalName(), element}});
+ Q_ASSERT(equals(listType->valueType(), elementType));
+ m_typeTracker->listTypes[elementType] = listType;
+ return listType;
+ }
+ default:
+ break;
+ }
+ return QQmlJSScope::ConstPtr();
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
{
return m_imports[QmlIR::IRBuilder::asString(type->typeId)].scope;
@@ -224,12 +258,12 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::Add: {
const auto leftContents = containedType(left);
const auto rightContents = containedType(right);
- if (leftContents == stringType() || rightContents == stringType())
+ if (equals(leftContents, stringType()) || equals(rightContents, stringType()))
return QQmlJSRegisterContent::create(stringType(), stringType(),
QQmlJSRegisterContent::Builtin);
const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -241,7 +275,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
}
case QSOperator::Op::Sub: {
const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right));
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -267,9 +301,8 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
return merge(left, right);
}
-QQmlJSRegisterContent
-QQmlJSTypeResolver::typeForUnaryOperation(UnaryOperator oper,
- const QQmlJSRegisterContent &operand) const
+QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
+ UnaryOperator oper, const QQmlJSRegisterContent &operand) const
{
// For now, we are only concerned with the unary arithmetic operators.
// The boolean and bitwise ones are special cased elsewhere.
@@ -291,16 +324,16 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
- return type == m_intType || type == m_realType || type == m_floatType || type == m_boolType
- || type == m_voidType || type == m_nullType || type == m_stringType
- || type == m_jsPrimitiveType;
+ return equals(type, m_intType) || equals(type, m_realType) || equals(type, m_floatType)
+ || equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType)
+ || equals(type, m_stringType) || equals(type, m_jsPrimitiveType);
}
bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
{
return searchBaseAndExtensionTypes(
type, [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension) {
- return scope == m_numberPrototype;
+ return equals(scope, m_numberPrototype);
});
}
@@ -311,19 +344,162 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
return container.type();
if (container.isProperty()) {
const QQmlJSMetaProperty prop = container.property();
- return prop.isList() ? listPropertyType() : QQmlJSScope::ConstPtr(prop.type());
+ return prop.isList() ? listType(prop.type()) : QQmlJSScope::ConstPtr(prop.type());
}
if (container.isEnumeration())
return container.enumeration().type();
if (container.isMethod())
- return jsValueType();
- if (container.isImportNamespace())
- return container.scopeType();
+ return container.storedType(); // Methods can only be stored in QJSValue.
+ if (container.isImportNamespace()) {
+ switch (container.variant()) {
+ case QQmlJSRegisterContent::ScopeModulePrefix:
+ return container.storedType(); // We don't store scope module prefixes
+ case QQmlJSRegisterContent::ObjectModulePrefix:
+ return container.scopeType(); // We need to pass the original object through.
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+ if (container.isConversion())
+ return container.conversionResult();
+
+ Q_UNREACHABLE();
+ return {};
+}
+
+void QQmlJSTypeResolver::trackListPropertyType(
+ const QQmlJSScope::ConstPtr &trackedListElementType) const
+{
+ if (m_typeTracker->trackedTypes.contains(trackedListElementType)
+ && !m_typeTracker->listTypes.contains(trackedListElementType)) {
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(m_listPropertyType);
+ m_typeTracker->listTypes[trackedListElementType] = clone;
+ m_typeTracker->trackedTypes[clone] = { m_listPropertyType, QQmlJSScope::ConstPtr(), clone };
+ }
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedType(const QQmlJSScope::ConstPtr &type) const
+{
+ // If origin is in fact an already tracked type, track the original of that one instead.
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ QQmlJSScope::ConstPtr orig = (it == m_typeTracker->trackedTypes.end()) ? type : it->original;
+
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(orig);
+ m_typeTracker->trackedTypes[clone] = { std::move(orig), QQmlJSScope::ConstPtr(), clone };
+ return clone;
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
+ const QQmlJSRegisterContent &origin,
+ QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const
+{
+ if (origin.isType()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), (this->*op)(origin.type()),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isProperty()) {
+ QQmlJSMetaProperty prop = origin.property();
+ prop.setType((this->*op)(prop.type()));
+ if (prop.isList())
+ trackListPropertyType(prop.type());
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), prop,
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isEnumeration()) {
+ QQmlJSMetaEnum enumeration = origin.enumeration();
+ enumeration.setType((this->*op)(enumeration.type()));
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), enumeration, origin.enumMember(),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isMethod()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.method(), origin.variant(),
+ (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isImportNamespace()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.importNamespace(),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
+
+ if (origin.isConversion()) {
+ return QQmlJSRegisterContent::create(
+ (this->*op)(origin.storedType()), origin.conversionOrigins(),
+ (this->*op)(origin.conversionResult()),
+ origin.variant(), (this->*op)(origin.scopeType()));
+ }
Q_UNREACHABLE();
return {};
}
+QQmlJSRegisterContent QQmlJSTypeResolver::original(const QQmlJSRegisterContent &type) const
+{
+ return transformed(type, &QQmlJSTypeResolver::originalType);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::tracked(const QQmlJSRegisterContent &type) const
+{
+ return transformed(type, &QQmlJSTypeResolver::trackedType);
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedContainedType(
+ const QQmlJSRegisterContent &container) const
+{
+ const QQmlJSScope::ConstPtr type = containedType(container);
+ return m_typeTracker->trackedTypes.contains(type) ? type : QQmlJSScope::ConstPtr();
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
+ const QQmlJSRegisterContent &container) const
+{
+ return originalType(containedType(container));
+}
+
+void QQmlJSTypeResolver::adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(tracked);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ it->replacement = comparableType(conversion);
+ *it->clone = std::move(*QQmlJSScope::clone(conversion));
+}
+
+void QQmlJSTypeResolver::adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked, const QList<QQmlJSScope::ConstPtr> &conversions) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(tracked);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ QQmlJSScope::Ptr mutableTracked = it->clone;
+ QQmlJSScope::ConstPtr result;
+ for (const QQmlJSScope::ConstPtr &type : conversions)
+ result = merge(type, result);
+
+ // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
+ // we better not change the type.
+ if (canPrimitivelyConvertFromTo(tracked, result)) {
+ it->replacement = comparableType(result);
+ *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ }
+}
+
+void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ Q_ASSERT(it != m_typeTracker->trackedTypes.end());
+ *it->clone = std::move(*QQmlJSScope::clone(genericType(type)));
+ if (it->replacement)
+ it->replacement = genericType(it->replacement);
+ it->original = genericType(it->original);
+}
+
QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &container) const
{
QQmlJSScope::ConstPtr type;
@@ -345,69 +521,28 @@ QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &conta
bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
- // ### need a generic solution for custom cpp types:
- // if (from->m_hasBoolOverload && to == boolType)
- // return true;
-
- if (from == to)
- return true;
- if (from == m_varType || to == m_varType)
- return true;
- if (from == m_jsValueType || to == m_jsValueType)
- return true;
- if (isNumeric(from) && isNumeric(to))
- return true;
- if (from == m_intType && to == m_boolType)
- return true;
- if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference && to == m_boolType)
- return true;
-
- // Yes, our String has number constructors.
- if (isNumeric(from) && to == m_stringType)
+ if (canPrimitivelyConvertFromTo(from, to))
return true;
- // We can always convert between strings and urls.
- if ((from == m_stringType && to == m_urlType) || (from == m_urlType && to == m_stringType))
- return true;
+ // ### need a generic solution for custom cpp types:
+ // if (from->m_hasBoolOverload && equals(to, boolType))
+ // return true;
// All of these types have QString conversions that require a certain format
- // TODO: Actually verify these strings or deprecate them
- if (from == m_stringType && !to.isNull()) {
+ // TODO: Actually verify these strings or deprecate them.
+ // Some of those type are builtins or should be builtins. We should add code for them
+ // in QQmlJSCodeGenerator::conversion().
+ if (equals(from, m_stringType) && !to.isNull()) {
const QString toTypeName = to->internalName();
- if (to == m_dateTimeType || toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
- || toTypeName == u"QPoint"_qs || toTypeName == u"QPointF"_qs
- || toTypeName == u"QSize"_qs || toTypeName == u"QSizeF"_qs || toTypeName == u"QRect"_qs
- || toTypeName == u"QRectF"_qs || toTypeName == u"QColor"_qs) {
+ if (toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
+ || toTypeName == u"QPoint"_qs || toTypeName == u"QPointF"_qs
+ || toTypeName == u"QSize"_qs || toTypeName == u"QSizeF"_qs
+ || toTypeName == u"QRect"_qs || toTypeName == u"QRectF"_qs
+ || toTypeName == u"QColor"_qs) {
return true;
}
}
- if (from == m_voidType)
- return true;
-
- if (to.isNull())
- return false;
-
- if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- return true;
-
- if (from == m_jsPrimitiveType) {
- // You can cast any primitive to a nullptr
- return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
- }
-
- if (to == m_jsPrimitiveType)
- return isPrimitive(from);
-
- const bool matchByName = !to->isComposite();
- Q_ASSERT(!matchByName || !to->internalName().isEmpty());
- for (auto baseType = from; baseType; baseType = baseType->baseType()) {
- if (baseType == to)
- return true;
- if (matchByName && baseType->internalName() == to->internalName())
- return true;
- }
-
return false;
}
@@ -426,38 +561,61 @@ static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent
QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
const QQmlJSRegisterContent &b) const
{
- return QQmlJSRegisterContent::create(
- merge(a.storedType(), b.storedType()),
- merge(containedType(a), containedType(b)), mergeVariants(a.variant(), b.variant()),
- merge(a.scopeType(), b.scopeType()));
-}
+ QList<QQmlJSScope::ConstPtr> origins;
+ if (a.isConversion())
+ origins.append(a.conversionOrigins());
+ else
+ origins.append(containedType(a));
-static QQmlJSScope::ConstPtr commonBaseType(const QQmlJSScope::ConstPtr &a,
- const QQmlJSScope::ConstPtr &b)
-{
- for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
- for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
- if (aBase == bBase)
- return aBase;
- }
- }
+ if (b.isConversion())
+ origins.append(b.conversionOrigins());
+ else
+ origins.append(containedType(b));
- return {};
+ std::sort(origins.begin(), origins.end());
+ const auto erase = std::unique(origins.begin(), origins.end());
+ origins.erase(erase, origins.end());
+
+ return QQmlJSRegisterContent::create(
+ merge(a.storedType(), b.storedType()),
+ origins,
+ merge(containedType(a), containedType(b)),
+ mergeVariants(a.variant(), b.variant()),
+ merge(a.scopeType(), b.scopeType()));
}
QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const
{
- if (a == b)
+ if (a.isNull())
+ return b;
+
+ if (b.isNull())
+ return a;
+
+ const auto commonBaseType = [this](
+ const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
+ for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
+ for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
+ if (equals(aBase, bBase))
+ return aBase;
+ }
+ }
+
+ return QQmlJSScope::ConstPtr();
+ };
+
+
+ if (equals(a, b))
return a;
- if (a == jsValueType() || a == varType())
+ if (equals(a, jsValueType()) || equals(a, varType()))
return a;
- if (b == jsValueType() || b == varType())
+ if (equals(b, jsValueType()) || equals(b, varType()))
return b;
auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) {
- return (a == from && b == to) || (b == from && a == to);
+ return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to));
};
if (isNumeric(a) && isNumeric(b))
@@ -473,22 +631,44 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (auto commonBase = commonBaseType(a, b))
return commonBase;
- if (a == nullType() && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(a, nullType()) && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return b;
- if (b == nullType() && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(b, nullType()) && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return a;
return varType();
}
+bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content) const
+{
+ const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
+ return equals(type, m_voidType) || equals(type, m_varType)
+ || equals(type, m_jsValueType) || equals(type, m_jsPrimitiveType);
+ };
+
+ if (!canBeUndefined(content.storedType()))
+ return false;
+
+ if (!content.isConversion())
+ return canBeUndefined(containedType(content));
+
+ const auto origins = content.conversionOrigins();
+ for (const auto &origin : origins) {
+ if (canBeUndefined(origin))
+ return true;
+ }
+
+ return false;
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPtr &type,
ComponentIsGeneric allowComponent) const
{
if (type->isScript())
return m_jsValueType;
- if (type == m_metaObjectType)
+ if (equals(type, m_metaObjectType))
return m_metaObjectType;
if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
@@ -506,21 +686,19 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
}
}
- m_logger->logWarning(u"Object type %1 is not derived from QObject or QQmlComponent"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
// Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
// We don't want to deal with those, but m_jsValueType is the best generic option.
return m_jsValueType;
}
- if (type == voidType())
- return jsPrimitiveType();
-
- if (isPrimitive(type) || type == m_jsValueType || type == m_listPropertyType
- || type == m_urlType || type == m_dateTimeType || type == m_variantListType
- || type == m_varType) {
+ if (isPrimitive(type) || equals(type, m_jsValueType) || equals(type, m_listPropertyType)
+ || equals(type, m_urlType) || equals(type, m_dateTimeType)
+ || equals(type, m_variantListType) || equals(type, m_varType)
+ || equals(type, m_stringListType) || equals(type, m_emptyListType)) {
return type;
}
@@ -530,6 +708,10 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
if (type->scopeType() == QQmlJSScope::EnumScope)
return m_intType;
+ if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ return listType(genericType(type->valueType()));
+ }
+
return m_varType;
}
@@ -552,46 +734,6 @@ scopeContentVariant(QQmlJSTypeResolver::BaseOrExtension mode, bool isMethod)
return QQmlJSRegisterContent::Unknown;
}
-static bool isAssignedToDefaultProperty(const QQmlJSScope::ConstPtr &parent,
- const QQmlJSScope::ConstPtr &child)
-{
- QString defaultPropertyName;
- QQmlJSMetaProperty defaultProperty;
- if (!searchBaseAndExtensionTypes(
- parent, [&](const QQmlJSScope::ConstPtr &scope,
- QQmlJSTypeResolver::BaseOrExtension mode) {
- Q_UNUSED(mode);
- defaultPropertyName = scope->defaultPropertyName();
- defaultProperty = scope->property(defaultPropertyName);
- return !defaultPropertyName.isEmpty();
- })) {
- return false;
- }
-
- QQmlJSScope::ConstPtr bindingHolder = parent;
- while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
- // Only traverse the base type hierarchy here, not the extended types.
- // Extensions cannot hold bindings.
- bindingHolder = bindingHolder->baseType();
-
- // Consequently, the default property may be inaccessibly
- // hidden in some extension via shadowing.
- // Nothing can be assigned to it then.
- if (!bindingHolder)
- return false;
- }
-
- const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
- = bindingHolder->propertyBindings(defaultPropertyName);
- for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
- && binding.objectType() == child) {
- return true;
- }
- }
- return false;
-}
-
static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isComposite());
@@ -612,6 +754,46 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s
QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
const QString &name) const
{
+ const auto isAssignedToDefaultProperty = [this](
+ const QQmlJSScope::ConstPtr &parent, const QQmlJSScope::ConstPtr &child) {
+ QString defaultPropertyName;
+ QQmlJSMetaProperty defaultProperty;
+ if (!searchBaseAndExtensionTypes(
+ parent, [&](const QQmlJSScope::ConstPtr &scope,
+ QQmlJSTypeResolver::BaseOrExtension mode) {
+ Q_UNUSED(mode);
+ defaultPropertyName = scope->defaultPropertyName();
+ defaultProperty = scope->property(defaultPropertyName);
+ return !defaultPropertyName.isEmpty();
+ })) {
+ return false;
+ }
+
+ QQmlJSScope::ConstPtr bindingHolder = parent;
+ while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
+ // Only traverse the base type hierarchy here, not the extended types.
+ // Extensions cannot hold bindings.
+ bindingHolder = bindingHolder->baseType();
+
+ // Consequently, the default property may be inaccessibly
+ // hidden in some extension via shadowing.
+ // Nothing can be assigned to it then.
+ if (!bindingHolder)
+ return false;
+ }
+
+ const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
+ = bindingHolder->propertyBindings(defaultPropertyName);
+ for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
+ if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
+ && equals(binding.objectType(), child)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
if (QQmlJSScope::ConstPtr identified = scopeForId(name, scope)) {
return QQmlJSRegisterContent::create(storedType(identified), identified,
QQmlJSRegisterContent::ObjectById, scope);
@@ -673,15 +855,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
if (const auto attached = type->attachedType()) {
if (!genericType(attached)) {
- m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg(
- attached->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot resolve generic base of attached %1"_qs.arg(
+ attached->internalName()),
+ Log_Compiler, attached->sourceLocation());
return {};
} else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
- u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
return {};
} else {
// We don't know yet whether we need the attached or the plain object. In direct
@@ -746,6 +927,71 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
return false;
}
+bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
+ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
+{
+ if (equals(from, to))
+ return true;
+ if (equals(from, m_varType) || equals(to, m_varType))
+ return true;
+ if (equals(from, m_jsValueType) || equals(to, m_jsValueType))
+ return true;
+ if (isNumeric(from) && isNumeric(to))
+ return true;
+ if (equals(from, m_intType) && equals(to, m_boolType))
+ return true;
+ if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
+ && equals(to, m_boolType)) {
+ return true;
+ }
+
+ // Yes, our String has number constructors.
+ if (isNumeric(from) && equals(to, m_stringType))
+ return true;
+
+ // We can always convert between strings and urls.
+ if ((equals(from, m_stringType) && equals(to, m_urlType))
+ || (equals(from, m_urlType) && equals(to, m_stringType))) {
+ return true;
+ }
+
+ if (equals(from, m_voidType) || equals(to, m_voidType))
+ return true;
+
+ if (to.isNull())
+ return false;
+
+ if (equals(from, m_stringType) && equals(to, m_dateTimeType))
+ return true;
+
+ if (equals(from, m_nullType)
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ return true;
+ }
+
+ if (equals(from, m_jsPrimitiveType)) {
+ // You can cast any primitive to a nullptr
+ return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
+ }
+
+ if (equals(to, m_jsPrimitiveType))
+ return isPrimitive(from);
+
+ if (equals(from, m_emptyListType))
+ return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
+
+ const bool matchByName = !to->isComposite();
+ Q_ASSERT(!matchByName || !to->internalName().isEmpty());
+ for (auto baseType = from; baseType; baseType = baseType->baseType()) {
+ if (equals(baseType, to))
+ return true;
+ if (matchByName && baseType->internalName() == to->internalName())
+ return true;
+ }
+
+ return false;
+}
+
QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
bool isWritable, const QQmlJSScope::ConstPtr &scope) const
{
@@ -762,7 +1008,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
{
QQmlJSRegisterContent result;
- if (type == jsValueType()) {
+ if (equals(type, jsValueType())) {
QQmlJSMetaProperty prop;
prop.setPropertyName(name);
prop.setTypeName(u"QJSValue"_qs);
@@ -772,9 +1018,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
QQmlJSRegisterContent::JavaScriptObjectProperty, type);
}
- if ((type == stringType() || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ if ((equals(type, stringType())
+ || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
&& name == u"length"_qs) {
- return lengthProperty(type != stringType(), type);
+ return lengthProperty(!equals(type, stringType()), type);
}
const auto check = [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension mode) {
@@ -821,15 +1068,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
if (!genericType(attached)) {
- m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg(
- attached->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot resolve generic base of attached %1"_qs.arg(
+ attached->internalName()),
+ Log_Compiler, attached->sourceLocation());
return {};
} else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
- u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
- type->internalName()),
- Log_Compiler);
+ m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_qs.arg(
+ type->internalName()),
+ Log_Compiler, type->sourceLocation());
return {};
} else {
return QQmlJSRegisterContent::create(storedType(attached), attached,
@@ -895,19 +1141,22 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
}
if (type.isImportNamespace()) {
if (type.scopeType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- m_logger->logWarning(
+ m_logger->log(
u"Cannot use non-reference type %1 as base of namespaced attached type"_qs.arg(
type.scopeType()->internalName()),
- Log_Type);
+ Log_Type, type.scopeType()->sourceLocation());
return {};
}
if (QQmlJSScope::ConstPtr result = typeForName(name)) {
QQmlJSScope::ConstPtr attached = result->attachedType();
if (attached && genericType(attached)) {
- return QQmlJSRegisterContent::create(storedType(attached), attached,
- QQmlJSRegisterContent::ObjectAttached,
- result);
+ return QQmlJSRegisterContent::create(
+ storedType(attached), attached,
+ type.variant() == QQmlJSRegisterContent::ObjectModulePrefix
+ ? QQmlJSRegisterContent::ObjectAttached
+ : QQmlJSRegisterContent::ScopeAttached,
+ result);
}
if (result->isSingleton()) {
@@ -919,6 +1168,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return {};
}
+ if (type.isConversion()) {
+ const auto result = memberType(type.conversionResult(), name);
+ return result.isValid() ? result : memberEnumType(type.scopeType(), name);
+ }
Q_UNREACHABLE();
return {};
@@ -932,7 +1185,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
auto valueType = [this](const QQmlJSScope::ConstPtr &scope) {
if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return scope->valueType();
- else if (scope == m_jsValueType || scope == m_varType)
+ else if (equals(scope, m_jsValueType) || equals(scope, m_varType))
return m_jsValueType;
return QQmlJSScope::ConstPtr();
};
@@ -940,6 +1193,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
if (listType.isType()) {
scope = listType.type();
value = valueType(scope);
+ } else if (listType.isConversion()) {
+ value = valueType(listType.conversionResult());
} else if (listType.isProperty()) {
const auto prop = listType.property();
if (prop.isList()) {
@@ -970,19 +1225,35 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
return QQmlJSRegisterContent::create(stored, property, QQmlJSRegisterContent::ListValue, scope);
}
+QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
+ const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant) const
+{
+ Q_ASSERT(variant == QQmlJSRegisterContent::MethodReturnValue
+ || variant == QQmlJSRegisterContent::JavaScriptReturnValue);
+ return QQmlJSRegisterContent::create(storedType(type), type, variant);
+}
+
+bool QQmlJSTypeResolver::registerIsStoredIn(
+ const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(reg.storedType(), type);
+}
+
bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const
{
if (reg.isType())
- return reg.type() == type;
+ return equals(reg.type(), type);
+ if (reg.isConversion())
+ return equals(reg.conversionResult(), type);
if (reg.isProperty()) {
const auto prop = reg.property();
- return prop.isList() ? type == listPropertyType() : prop.type() == type;
+ return prop.isList() ? equals(type, listPropertyType()) : equals(prop.type(), type);
}
if (reg.isEnumeration())
- return type == intType();
+ return equals(type, intType());
if (reg.isMethod())
- return type == jsValueType();
+ return equals(type, jsValueType());
return false;
}
@@ -990,8 +1261,8 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
{
if (type.isNull())
return {};
- if (type == voidType())
- return jsPrimitiveType();
+ if (equals(type, voidType()))
+ return type;
if (type->isScript())
return jsValueType();
if (type->isComposite()) {
@@ -1001,9 +1272,50 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
// If we can't find the non-composite base, we really don't know what it is.
return genericType(type);
}
- if (type->fileName().isEmpty())
+ if (type->filePath().isEmpty())
return genericType(type);
return type;
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(type);
+ return it == m_typeTracker->trackedTypes.end() ? type : it->original;
+}
+
+/*!
+ * \internal
+ *
+ * Compares the origin types of \a a and \a b. A straight a == b would compare the identity
+ * of the pointers. However, since we clone types to keep track of them, we need a separate
+ * way to compare the clones. Usually you'd do *a == *b for that, but as QQmlJSScope is rather
+ * large, we offer an optimization here that uses the type tracking we already have in place.
+ */
+bool QQmlJSTypeResolver::equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
+{
+ return comparableType(a) == comparableType(b);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::convert(
+ const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const
+{
+ if (from.isConversion()) {
+ return QQmlJSRegisterContent::create(
+ to.storedType(), from.conversionOrigins(), containedType(to), from.variant(),
+ from.scopeType());
+ }
+
+ return QQmlJSRegisterContent::create(
+ to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
+ containedType(to), from.variant(), from.scopeType());
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::comparableType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto it = m_typeTracker->trackedTypes.constFind(type);
+ if (it == m_typeTracker->trackedTypes.constEnd())
+ return type;
+ return it->replacement ? it->replacement : it->original;
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index babc0c6c22..32c64b89f7 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -62,12 +62,14 @@ public:
void init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program);
QQmlJSScope::ConstPtr voidType() const { return m_voidType; }
+ QQmlJSScope::ConstPtr emptyListType() const { return m_emptyListType; }
QQmlJSScope::ConstPtr nullType() const { return m_nullType; }
QQmlJSScope::ConstPtr realType() const { return m_realType; }
QQmlJSScope::ConstPtr floatType() const { return m_floatType; }
QQmlJSScope::ConstPtr intType() const { return m_intType; }
QQmlJSScope::ConstPtr boolType() const { return m_boolType; }
QQmlJSScope::ConstPtr stringType() const { return m_stringType; }
+ QQmlJSScope::ConstPtr stringListType() const { return m_stringListType; }
QQmlJSScope::ConstPtr urlType() const { return m_urlType; }
QQmlJSScope::ConstPtr dateTimeType() const { return m_dateTimeType; }
QQmlJSScope::ConstPtr variantListType() const { return m_variantListType; }
@@ -87,6 +89,7 @@ public:
return m_imports.contains(name) && !m_imports[name].scope;
}
+ QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType) const;
QQmlJSScope::ConstPtr typeForName(const QString &name) const { return m_imports[name].scope; }
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
@@ -96,8 +99,8 @@ public:
enum class UnaryOperator { Plus, Minus, Increment, Decrement };
- QQmlJSRegisterContent typeForUnaryOperation(UnaryOperator oper,
- const QQmlJSRegisterContent &operand) const;
+ QQmlJSRegisterContent typeForArithmeticUnaryOperation(
+ UnaryOperator oper, const QQmlJSRegisterContent &operand) const;
bool isPrimitive(const QQmlJSRegisterContent &type) const;
bool isNumeric(const QQmlJSRegisterContent &type) const;
@@ -116,17 +119,35 @@ public:
QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name) const;
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const;
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &listType) const;
+ QQmlJSRegisterContent returnType(const QQmlJSScope::ConstPtr &type,
+ QQmlJSRegisterContent::ContentVariant variant) const;
+ bool registerIsStoredIn(const QQmlJSRegisterContent &reg,
+ const QQmlJSScope::ConstPtr &type) const;
bool registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const;
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const;
QString containedTypeName(const QQmlJSRegisterContent &container) const;
+ QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &type) const;
+ QQmlJSRegisterContent original(const QQmlJSRegisterContent &type) const;
+
+ QQmlJSScope::ConstPtr trackedContainedType(const QQmlJSRegisterContent &container) const;
+ QQmlJSScope::ConstPtr originalContainedType(const QQmlJSRegisterContent &container) const;
+
+ void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
+ const QQmlJSScope::ConstPtr &conversion) const;
+ void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
+ const QList<QQmlJSScope::ConstPtr> &conversions) const;
+ void generalizeType(const QQmlJSScope::ConstPtr &type) const;
+
void setParentMode(ParentMode mode) { m_parentMode = mode; }
ParentMode parentMode() const { return m_parentMode; }
- QQmlJSScope::ConstPtr
- storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr originalType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr trackedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr comparableType(const QQmlJSScope::ConstPtr &type) const;
const QQmlJSScopesById &objectsById() const { return m_objectsById; }
const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers() const
@@ -134,10 +155,17 @@ public:
return m_signalHandlers;
}
-protected:
+ bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const;
+
+ QQmlJSRegisterContent convert(
+ const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const;
+
QQmlJSScope::ConstPtr merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const;
+ bool canHoldUndefined(const QQmlJSRegisterContent &content) const;
+protected:
+
QQmlJSRegisterContent memberType(const QQmlJSScope::ConstPtr &type, const QString &name) const;
QQmlJSRegisterContent memberEnumType(const QQmlJSScope::ConstPtr &type,
const QString &name) const;
@@ -145,9 +173,16 @@ protected:
bool isNumeric(const QQmlJSScope::ConstPtr &type) const;
bool checkEnums(const QQmlJSScope::ConstPtr &scope, const QString &name,
QQmlJSRegisterContent *result, BaseOrExtension mode) const;
+ bool canPrimitivelyConvertFromTo(
+ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const;
QQmlJSRegisterContent lengthProperty(bool isWritable, const QQmlJSScope::ConstPtr &scope) const;
+ void trackListPropertyType(const QQmlJSScope::ConstPtr &trackedListElementType) const;
+ QQmlJSRegisterContent transformed(
+ const QQmlJSRegisterContent &origin,
+ QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const;
QQmlJSScope::ConstPtr m_voidType;
+ QQmlJSScope::ConstPtr m_emptyListType;
QQmlJSScope::ConstPtr m_nullType;
QQmlJSScope::ConstPtr m_numberPrototype;
QQmlJSScope::ConstPtr m_realType;
@@ -155,6 +190,7 @@ protected:
QQmlJSScope::ConstPtr m_intType;
QQmlJSScope::ConstPtr m_boolType;
QQmlJSScope::ConstPtr m_stringType;
+ QQmlJSScope::ConstPtr m_stringListType;
QQmlJSScope::ConstPtr m_urlType;
QQmlJSScope::ConstPtr m_dateTimeType;
QQmlJSScope::ConstPtr m_variantListType;
@@ -172,6 +208,27 @@ protected:
ParentMode m_parentMode = UseParentProperty;
QQmlJSLogger *m_logger = nullptr;
+
+ struct TrackedType
+ {
+ // The type originally found via type analysis.
+ QQmlJSScope::ConstPtr original;
+
+ // Any later replacement used to overwrite the contents of the clone.
+ QQmlJSScope::ConstPtr replacement;
+
+ // A clone of original, used to track the type,
+ // contents possibly overwritten by replacement.
+ QQmlJSScope::Ptr clone;
+ };
+
+ struct TypeTracker
+ {
+ QHash<QQmlJSScope::ConstPtr, QQmlJSScope::Ptr> listTypes;
+ QHash<QQmlJSScope::ConstPtr, TrackedType> trackedTypes;
+ };
+
+ std::unique_ptr<TypeTracker> m_typeTracker;
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp
index 81b69d1216..0b078b4868 100644
--- a/src/qmldebug/qqmldebugconnection.cpp
+++ b/src/qmldebug/qqmldebugconnection.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qeventloop.h>
#include <QtCore/qtimer.h>
+#include <QtCore/QHash>
#include <QtCore/qdatastream.h>
#include <QtNetwork/qlocalserver.h>
#include <QtNetwork/qlocalsocket.h>
diff --git a/src/qmldebug/qqmlenginecontrolclient_p_p.h b/src/qmldebug/qqmlenginecontrolclient_p_p.h
index 597ae23e3a..ec2cbd31f0 100644
--- a/src/qmldebug/qqmlenginecontrolclient_p_p.h
+++ b/src/qmldebug/qqmlenginecontrolclient_p_p.h
@@ -43,6 +43,8 @@
#include "qqmlenginecontrolclient_p.h"
#include "qqmldebugclient_p_p.h"
+#include <QtCore/QHash>
+
//
// W A R N I N G
// -------------
diff --git a/src/qmldom/qqmldomlinewriter_p.h b/src/qmldom/qqmldomlinewriter_p.h
index 2808282034..544a88fe70 100644
--- a/src/qmldom/qqmldomlinewriter_p.h
+++ b/src/qmldom/qqmldomlinewriter_p.h
@@ -55,6 +55,7 @@
#include <QtQml/private/qqmljssourcelocation_p.h>
#include <QtCore/QObject>
#include <QtCore/QAtomicInt>
+#include <QtCore/QMap>
#include <functional>
QT_BEGIN_NAMESPACE
diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp
index 3a946800d4..716da48bbf 100644
--- a/src/qmldom/qqmldomtypesreader.cpp
+++ b/src/qmldom/qqmldomtypesreader.cpp
@@ -174,7 +174,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
++it;
}
}
- comp.setFileName(jsScope->fileName());
+ comp.setFileName(jsScope->filePath());
comp.setName(jsScope->internalName());
m_currentPath = m_currentPath.key(comp.name())
.index(qmltypesFilePtr()->components().values(comp.name()).length());
diff --git a/src/qmlintegration/qqmlintegration.h b/src/qmlintegration/qqmlintegration.h
index d2d7b186ff..9aae01830b 100644
--- a/src/qmlintegration/qqmlintegration.h
+++ b/src/qmlintegration/qqmlintegration.h
@@ -50,6 +50,8 @@ namespace QQmlPrivate {
template<class> struct QmlAttachedAccessor;
template<class, class> struct QmlExtended;
template<typename, typename> struct QmlInterface;
+ template<class, class>
+ struct QmlExtendedNamespace;
}
template <typename T> class QList;
diff --git a/src/qmlmodels/CMakeLists.txt b/src/qmlmodels/CMakeLists.txt
index ccee022672..1f8ffcdadf 100644
--- a/src/qmlmodels/CMakeLists.txt
+++ b/src/qmlmodels/CMakeLists.txt
@@ -29,6 +29,11 @@ qt_internal_add_qml_module(QmlModels
GENERATE_PRIVATE_CPP_EXPORTS
)
+qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_itemmodel
+ SOURCES
+ qqmlmodelindexvaluetype.cpp qqmlmodelindexvaluetype_p.h
+)
+
qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_object_model
SOURCES
qqmlinstantiator.cpp qqmlinstantiator_p.h
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index 9ee9c91cd4..1463a84af9 100644
--- a/src/qmlmodels/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -953,7 +953,8 @@ QQmlAdaptorModel::Accessors::~Accessors()
}
QQmlAdaptorModel::QQmlAdaptorModel()
- : accessors(&qt_vdm_null_accessors)
+ : QQmlGuard<QObject>(QQmlAdaptorModel::objectDestroyedImpl, nullptr)
+ , accessors(&qt_vdm_null_accessors)
{
}
@@ -1046,9 +1047,10 @@ void QQmlAdaptorModel::useImportVersion(QTypeRevision revision)
modelItemRevision = revision;
}
-void QQmlAdaptorModel::objectDestroyed(QObject *)
+void QQmlAdaptorModel::objectDestroyedImpl(QQmlGuardImpl *guard)
{
- setModel(QVariant());
+ auto This = static_cast<QQmlAdaptorModel *>(guard);
+ This->setModel(QVariant());
}
QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
diff --git a/src/qmlmodels/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h
index 58991fbb15..06fe6068d7 100644
--- a/src/qmlmodels/qqmladaptormodel_p.h
+++ b/src/qmlmodels/qqmladaptormodel_p.h
@@ -170,8 +170,8 @@ public:
inline bool canFetchMore() const { return accessors->canFetchMore(*this); }
inline void fetchMore() { return accessors->fetchMore(*this); }
-protected:
- void objectDestroyed(QObject *) override;
+private:
+ static void objectDestroyedImpl(QQmlGuardImpl *);
};
class QQmlAdaptorModelProxyInterface
diff --git a/src/qml/types/qqmlmodelindexvaluetype.cpp b/src/qmlmodels/qqmlmodelindexvaluetype.cpp
index cbf2fef348..cbf2fef348 100644
--- a/src/qml/types/qqmlmodelindexvaluetype.cpp
+++ b/src/qmlmodels/qqmlmodelindexvaluetype.cpp
diff --git a/src/qml/types/qqmlmodelindexvaluetype_p.h b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
index 2c37d91c71..2c37d91c71 100644
--- a/src/qml/types/qqmlmodelindexvaluetype_p.h
+++ b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel.cpp b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
index 06a8c5c6d6..a2eac512ec 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel.cpp
+++ b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
@@ -517,6 +517,32 @@ void QQmlTreeModelToTableModel::expandRow(int n)
expandPendingRows();
}
+void QQmlTreeModelToTableModel::expandRecursively(int row, int depth)
+{
+ Q_ASSERT(depth == -1 || depth > 0);
+ const int startDepth = depthAtRow(row);
+
+ auto expandHelp = [=] (const auto expandHelp, const QModelIndex &index) -> void {
+ const int rowToExpand = itemIndex(index);
+ if (!m_expandedItems.contains(index))
+ expandRow(rowToExpand);
+
+ if (depth != -1 && depthAtRow(rowToExpand) == startDepth + depth - 1)
+ return;
+
+ const int childCount = m_model->rowCount(index);
+ for (int childRow = 0; childRow < childCount; ++childRow) {
+ const QModelIndex childIndex = m_model->index(childRow, 0, index);
+ if (m_model->hasChildren(childIndex))
+ expandHelp(expandHelp, childIndex);
+ }
+ };
+
+ const QModelIndex index = m_items[row].index;
+ if (index.isValid())
+ expandHelp(expandHelp, index);
+}
+
void QQmlTreeModelToTableModel::expandPendingRows(bool doInsertRows)
{
while (!m_itemsToExpand.isEmpty()) {
@@ -537,6 +563,30 @@ void QQmlTreeModelToTableModel::expandPendingRows(bool doInsertRows)
}
}
+void QQmlTreeModelToTableModel::collapseRecursively(int row)
+{
+ auto collapseHelp = [=] (const auto collapseHelp, const QModelIndex &index) -> void {
+ if (m_expandedItems.contains(index)) {
+ const int rowToCollapse = itemIndex(index);
+ if (rowToCollapse != -1)
+ collapseRow(rowToCollapse);
+ else
+ m_expandedItems.remove(index);
+ }
+
+ const int childCount = m_model->rowCount(index);
+ for (int childRow = 0; childRow < childCount; ++childRow) {
+ const QModelIndex childIndex = m_model->index(childRow, 0, index);
+ if (m_model->hasChildren(childIndex))
+ collapseHelp(collapseHelp, childIndex);
+ }
+ };
+
+ const QModelIndex index = m_items[row].index;
+ if (index.isValid())
+ collapseHelp(collapseHelp, index);
+}
+
void QQmlTreeModelToTableModel::collapseRow(int n)
{
if (!m_model || !isExpanded(n))
@@ -558,8 +608,13 @@ void QQmlTreeModelToTableModel::collapseRow(int n)
removeVisibleRows(n + 1, lastIndex);
}
-int QQmlTreeModelToTableModel::lastChildIndex(const QModelIndex &index)
+int QQmlTreeModelToTableModel::lastChildIndex(const QModelIndex &index) const
{
+ // The purpose of this function is to return the row of the last decendant of a node N.
+ // But note: index should point to the last child of N, and not N itself!
+ // This means that if index is not expanded, the last child will simply be index itself.
+ // Otherwise, since the tree underneath index can be of any depth, it will instead find
+ // the first sibling of N, get its table row, and simply return the row above.
if (!m_expandedItems.contains(index))
return itemIndex(index);
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
index a772b980e3..f86e135718 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
+++ b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
@@ -113,7 +113,7 @@ public:
int itemIndex(const QModelIndex &index) const;
void expandPendingRows(bool doInsertRows = true);
- int lastChildIndex(const QModelIndex &index);
+ int lastChildIndex(const QModelIndex &index) const;
void removeVisibleRows(int startIndex, int endIndex, bool doRemoveRows = true);
void dump() const;
@@ -137,7 +137,9 @@ public slots:
bool hasSiblings(int row) const;
int depthAtRow(int row) const;
void expandRow(int n);
+ void expandRecursively(int row, int depth);
void collapseRow(int n);
+ void collapseRecursively(int row);
private slots:
void modelHasBeenDestroyed();
diff --git a/src/qmlmodels/qquickpackage.cpp b/src/qmlmodels/qquickpackage.cpp
index bf83afb329..999919562b 100644
--- a/src/qmlmodels/qquickpackage.cpp
+++ b/src/qmlmodels/qquickpackage.cpp
@@ -90,11 +90,14 @@ public:
struct DataGuard : public QQmlGuard<QObject>
{
- DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; }
+ DataGuard(QObject *obj, QList<DataGuard> *l) : QQmlGuard<QObject>(DataGuard::objectDestroyedImpl, nullptr), list(l) { (QQmlGuard<QObject>&)*this = obj; }
QList<DataGuard> *list;
- void objectDestroyed(QObject *) override {
+
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) {
+ auto This = static_cast<DataGuard *>(guard);
// we assume priv will always be destroyed after objectDestroyed calls
- list->removeOne(*this);
+ This->list->removeOne(*This);
}
};
diff --git a/src/qmltest/CMakeLists.txt b/src/qmltest/CMakeLists.txt
index 0a3dd4abd1..7f55ebc8b0 100644
--- a/src/qmltest/CMakeLists.txt
+++ b/src/qmltest/CMakeLists.txt
@@ -33,12 +33,12 @@ qt_internal_add_qml_module(QuickTest
Qt::CorePrivate
Qt::Gui
Qt::QmlPrivate
- Qt::Quick
Qt::QuickPrivate
Qt::TestPrivate
PUBLIC_LIBRARIES
Qt::Core
Qt::Test
+ Qt::Quick
PRIVATE_MODULE_INTERFACE
Qt::TestPrivate
)
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index aa81b0e180..4c8289dd22 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -436,7 +436,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
testPath = s;
}
-#if defined(Q_OS_ANDROID)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_INTEGRITY)
if (testPath.isEmpty())
testPath = QLatin1String(":/");
#endif
diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h
index 58b53d7b6b..83e604688c 100644
--- a/src/qmltest/quicktestresult_p.h
+++ b/src/qmltest/quicktestresult_p.h
@@ -143,7 +143,7 @@ public Q_SLOTS:
void warn(const QString &message, const QUrl &location, int line);
void ignoreWarning(const QJSValue &message);
- void failOnWarning(const QJSValue &message);
+ Q_REVISION(6, 3) void failOnWarning(const QJSValue &message);
void wait(int ms);
void sleep(int ms);
diff --git a/src/qmltest/quicktestutil.cpp b/src/qmltest/quicktestutil.cpp
index 25863c3842..fd6f294673 100644
--- a/src/qmltest/quicktestutil.cpp
+++ b/src/qmltest/quicktestutil.cpp
@@ -72,6 +72,8 @@ void QuickTestUtil::populateClipboardText(int lineCount)
for (int i = lineCount; i > 0; --i)
lines << fmt.arg(i).arg(i - 1);
QGuiApplication::clipboard()->setText(lines.join(u'\n'));
+#else
+ Q_UNUSED(lineCount)
#endif
}
diff --git a/src/qmltyperegistrar/qmltyperegistrar.cpp b/src/qmltyperegistrar/qmltyperegistrar.cpp
index 8b16340342..e5d1bb95d6 100644
--- a/src/qmltyperegistrar/qmltyperegistrar.cpp
+++ b/src/qmltyperegistrar/qmltyperegistrar.cpp
@@ -256,11 +256,20 @@ int main(int argc, char **argv)
fprintf(output, "\n\n");
+ // Keep this in sync with _qt_internal_get_escaped_uri in CMake
QString moduleAsSymbol = module;
- moduleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
+ moduleAsSymbol.replace(QRegularExpression(QStringLiteral("[^A-Za-z0-9]")), QStringLiteral("_"));
- const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
+ QString underscoredModuleAsSymbol = module;
+ underscoredModuleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
+
+ if (underscoredModuleAsSymbol != moduleAsSymbol
+ || underscoredModuleAsSymbol.isEmpty()
+ || underscoredModuleAsSymbol.front().isDigit()) {
+ qWarning() << module << "is an invalid QML module URI. You cannot import this.";
+ }
+ const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
fprintf(output,
"#if !defined(QT_STATIC)\n"
"#define Q_QMLTYPE_EXPORT Q_DECL_EXPORT\n"
@@ -319,8 +328,8 @@ int main(int argc, char **argv)
QString targetName = className;
QString extendedName;
bool seenQmlElement = false;
- QJsonArray classInfos = classDef.value(QLatin1String("classInfos")).toArray();
- for (const QJsonValueRef v : classInfos) {
+ const QJsonArray classInfos = classDef.value(QLatin1String("classInfos")).toArray();
+ for (const QJsonValueConstRef v : classInfos) {
const QString name = v[QStringLiteral("name")].toString();
if (name == QStringLiteral("QML.Element"))
seenQmlElement = true;
diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp
index 559b4c71d2..fdb6a220df 100644
--- a/src/qmltyperegistrar/qmltypescreator.cpp
+++ b/src/qmltyperegistrar/qmltypescreator.cpp
@@ -85,7 +85,23 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
m_qml.writeArrayBinding(QLatin1String("interfaces"), interfaces);
}
- if (collector.elementName.isEmpty())
+ if (!collector.deferredNames.isEmpty()) {
+ QStringList deferredNames;
+ for (const QString &name : collector.deferredNames)
+ deferredNames << enquote(name);
+
+ m_qml.writeArrayBinding(QLatin1String("deferredNames"), deferredNames);
+ }
+
+ if (!collector.immediateNames.isEmpty()) {
+ QStringList immediateNames;
+ for (const QString &name : collector.immediateNames)
+ immediateNames << enquote(name);
+
+ m_qml.writeArrayBinding(QLatin1String("immediateNames"), immediateNames);
+ }
+
+ if (collector.elementName.isEmpty()) // e.g. if QML_ANONYMOUS
return;
if (!collector.sequenceValueType.isEmpty()) {
@@ -125,22 +141,6 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (collector.hasCustomParser)
m_qml.writeScriptBinding(QLatin1String("hasCustomParser"), QLatin1String("true"));
- if (!collector.deferredNames.isEmpty()) {
- QStringList deferredNames;
- for (const QString &name : collector.deferredNames)
- deferredNames << enquote(name);
-
- m_qml.writeArrayBinding(QLatin1String("deferredNames"), deferredNames);
- }
-
- if (!collector.immediateNames.isEmpty()) {
- QStringList immediateNames;
- for (const QString &name : collector.immediateNames)
- immediateNames << enquote(name);
-
- m_qml.writeArrayBinding(QLatin1String("immediateNames"), immediateNames);
- }
-
m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects);
if (!collector.attachedType.isEmpty())
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 0f8733eb0f..a757ec9afa 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -509,6 +509,10 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_wheelevent
handlers/qquickwheelhandler_p_p.h
)
+qt_internal_extend_target(Quick CONDITION QT_FEATURE_im
+ SOURCES
+ util/qquickinputmethod.cpp util/qquickinputmethod_p.h
+)
qt_internal_create_tracepoints(Quick qtquick.tracepoints)
qt_internal_add_docs(Quick
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 933a5b5cbe..9a4ce960f1 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -448,6 +448,11 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const
return accessibleUnignoredChildren(item());
}
+static bool isTextRole(QAccessible::Role role)
+{
+ return role == QAccessible::EditableText || role == QAccessible::StaticText;
+}
+
QAccessible::State QAccessibleQuickItem::state() const
{
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
@@ -465,7 +470,7 @@ QAccessible::State QAccessibleQuickItem::state() const
state.offscreen = true;
if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && object()->property("checked").toBool())
state.checked = true;
- if (item()->activeFocusOnTab() || role() == QAccessible::EditableText)
+ if (item()->activeFocusOnTab() || isTextRole(role()))
state.focusable = true;
if (item()->hasActiveFocus())
state.focused = true;
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
index 15b8394ebf..cbbaa36c7b 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
@@ -30,17 +30,17 @@
\title Qt Quick Layouts
\brief A module with a set of QML elements that arrange QML items in a user interface.
- Qt Quick Layouts are a set of QML types used to arrange items in a user interface. In contrast
- to \l{Item Positioners}{positioners}, Qt Quick Layouts can also resize their items. This makes
- them well suited for resizable user interfaces. Since layouts are items they can consequently
- be nested.
+ Qt Quick Layouts are a set of QML types used to arrange items in a user
+ interface. In contrast to \l{Item Positioners}{positioners}, Qt Quick
+ Layouts can also resize their child items. This makes them well suited for
+ resizable user interfaces.
Visit the \l{Qt Quick Layouts Overview} page to get started.
\section1 Using the Module
- The QML types can be imported into your application by adding the following
- import statement in your \c {.qml} file.
+ The \l {Qt Quick Layouts QML Types}{QML types} can be imported into your
+ application by adding the following import statement in your \c {.qml} file.
\qml
import QtQuick.Layouts
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc
index 0df832cca9..0df832cca9 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-qmltypes.qdoc
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index eab3b041bb..f0c7b7cbf2 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -185,6 +185,7 @@ force the usage of a given loop. To verify which render loop is in use, enable
the \c qt.scenegraph.general \l {QLoggingCategory}{logging category}.
\section2 Threaded Render Loop ('threaded')
+\target threaded_render_loop
On many configurations, the scene graph rendering will happen on a
dedicated render thread. This is done to increase parallelism of
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 596a061c0d..5c35283a9b 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -80,7 +80,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype color
+ \qmlvaluetype color
\ingroup qtquickvaluetypes
\brief an ARGB color value.
\target colorvaluetypedocs
@@ -140,7 +140,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype font
+ \qmlvaluetype font
\ingroup qtquickvaluetypes
\brief a font value with the properties of QFont.
\target fontvaluetypedocs
@@ -256,7 +256,7 @@ available when you import \c QtQuick.
*/
/*!
- \qmlbasictype vector2d
+ \qmlvaluetype vector2d
\ingroup qtquickvaluetypes
\brief A vector2d type has x and y attributes.
@@ -384,7 +384,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype vector3d
+ \qmlvaluetype vector3d
\ingroup qtquickvaluetypes
\brief a value with x, y, and z attributes.
@@ -555,7 +555,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype vector4d
+ \qmlvaluetype vector4d
\ingroup qtquickvaluetypes
\brief A vector4d type has x, y, z and w attributes.
@@ -696,7 +696,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype quaternion
+ \qmlvaluetype quaternion
\ingroup qtquickvaluetypes
\brief A quaternion type has scalar, x, y, and z attributes.
@@ -856,7 +856,7 @@ console.log(c + " " + d); // false true
*/
/*!
- \qmlbasictype matrix4x4
+ \qmlvaluetype matrix4x4
\ingroup qtquickvaluetypes
\brief A matrix4x4 type is a 4-row and 4-column matrix
diff --git a/src/quick/doc/src/qt6-changes.qdoc b/src/quick/doc/src/qt6-changes.qdoc
index 58a7ebb3e1..defacd95a1 100644
--- a/src/quick/doc/src/qt6-changes.qdoc
+++ b/src/quick/doc/src/qt6-changes.qdoc
@@ -138,7 +138,7 @@
OpenGL. It will not be functional when using another graphics API, such as
Vulkan or Metal. Applications relying on QQuickWidget should force the usage
of OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\section2 Changes to QQuick* APIs
@@ -206,7 +206,7 @@
not be functional when using another graphics API, such as Vulkan or Metal.
Applications relying on QQuickFramebufferObject should force the usage of
OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\li QQuickRenderControl has a slightly changed API: grab() is now
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index a8665a3641..2e070fa8df 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -38,26 +38,25 @@ provides a visual canvas and includes types for creating and animating
visual components, receiving user input, creating data models and views
and delayed object instantiation.
-The Qt Quick module provides both a \l{Qt Quick QML Types}{QML API} which supplies
-QML types for creating user interfaces with the QML language, and a
+The Qt Quick module provides both a \l{Qt Quick QML Types}{QML API}, which
+supplies QML types for creating user interfaces with the QML language, and a
\l{Qt Quick C++ Classes}{C++ API} for extending QML applications with C++ code.
\note A set of Qt Quick-based UI controls is also available to create user
interfaces. See \l{Qt Quick Controls} for more information.
-For those new to QML and Qt Quick, please see
-\l{QML Applications}
-for an introduction to writing QML applications.
+If you're new to QML and Qt Quick, please see \l{QML Applications} for an
+introduction to writing QML applications.
\section1 Using the Module
\section2 QML API
-The QML types in Qt Quick are available through the \c QtQuick import. To use the
-types, add the following import statement to your .qml file:
+The QML types in Qt Quick are available through the \c QtQuick import. To use
+the types, add the following import statement to your .qml file:
\qml
-import QtQml
+import QtQuick
\endqml
\section2 C++ API
@@ -87,13 +86,13 @@ QT += quick
\section1 Important Concepts in Qt Quick
-Qt Quick provides everything needed to create a rich application with a fluid
-and dynamic user interface. It enables user interfaces to be built around the
+Qt Quick provides everything you need to create a rich application with a fluid
+and dynamic user interface. It enables you to build user interfaces around the
behavior of user interface components and how they connect with one another,
and it provides a visual canvas with its own coordinate system and rendering
-engine. Animation and transition effects are a first class concept in Qt Quick,
-and visual effects can be supplemented through specialized components for
-particle and shader effects.
+engine. Animation and transition effects are first class concepts in Qt Quick,
+and you can add visual effects through specialized components for particle and
+shader effects.
\list
\li \l{Important Concepts In Qt Quick - The Visual Canvas}{The Visual Canvas}
@@ -105,7 +104,7 @@ particle and shader effects.
\li \l{Important Concepts In Qt Quick - Convenience Types}{Convenience Types}
\endlist
-When using the \c QtQuick module, you will need to know how to write QML
+When using the Qt Quick module, you will need to know how to write QML
applications using the QML language. In particular, QML Basics and QML
Essentials from the \l{QML Applications} page.
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h
index ffb01563ed..dcfa863553 100644
--- a/src/quick/items/context2d/qquickcanvascontext_p.h
+++ b/src/quick/items/context2d/qquickcanvascontext_p.h
@@ -56,10 +56,14 @@
QT_REQUIRE_CONFIG(quick_canvas);
#include <QtQuick/qquickitem.h>
-#include <QtQml/private/qv4value_p.h>
+#include <QtQml/private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
+namespace QV4 {
+ struct ExecutionEngine;
+}
+
class QQuickCanvasItem;
class QSGLayer;
diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index 1972a9a507..11e1ec3fb2 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -67,6 +67,7 @@ class QQuickCanvasContext;
class QQuickCanvasItemPrivate;
class QQuickPixmap;
+class QQmlV4Function;
class QQuickCanvasPixmap : public QQmlRefCount
{
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index ea1354725f..d769c4396b 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(quick_canvas);
#include <QtCore/qqueue.h>
#include <QtCore/QWaitCondition>
-#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
//#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose!
@@ -78,6 +77,10 @@ QT_REQUIRE_CONFIG(quick_canvas);
QT_BEGIN_NAMESPACE
+namespace QV4 {
+ struct ExecutionEngine;
+}
+
class QQuickContext2DCommandBuffer;
class QQuickContext2DTexture;
class QQuickPixmap;
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 0b593c0089..730bbe4404 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -406,9 +406,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role)
m_state.focusable = true;
break;
case QAccessible::StaticText:
- if (!m_stateExplicitlySet.readOnly) {
+ if (!m_stateExplicitlySet.readOnly)
m_state.readOnly = true;
- }
+ if (!m_stateExplicitlySet.focusable)
+ m_state.focusable = true;
break;
default:
break;
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index e17a28d07e..a84af80ee9 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -73,11 +73,11 @@ class QQuickDragGrabber
class Item : public QQmlGuard<QQuickItem>
{
public:
- Item(QQuickItem *item) : QQmlGuard<QQuickItem>(item) {}
+ Item(QQuickItem *item) : QQmlGuard<QQuickItem>(Item::objectDestroyedImpl, item) {}
QIntrusiveListNode node;
- protected:
- void objectDestroyed(QQuickItem *) override { delete this; }
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) { delete static_cast<Item *>(guard); }
};
typedef QIntrusiveList<Item, &Item::node> ItemList;
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 41f682fe57..3cf91cdc8f 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -51,8 +51,8 @@
// We mean it.
//
-#include "qquickpainteditem.h"
#include <private/qtquickglobal_p.h>
+#include <QtQuick/qquickitem.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 197d891b2a..2fdb0f00c8 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -477,6 +477,7 @@ private:
friend class QSGRenderer;
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
+ friend class QQuickAnchorChanges;
Q_DISABLE_COPY(QQuickItem)
Q_DECLARE_PRIVATE(QQuickItem)
};
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index bde39d99bd..ea1aa7b0c9 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -1041,9 +1041,15 @@ void QQuickLoaderPrivate::createComponent()
const QQmlComponent::CompilationMode mode = asynchronous
? QQmlComponent::Asynchronous
: QQmlComponent::PreferSynchronous;
- QQmlContext *context = qmlContext(q);
- component.setObject(new QQmlComponent(
- context->engine(), context->resolvedUrl(source), mode, q), q);
+ if (QQmlContext *context = qmlContext(q)) {
+ if (QQmlEngine *engine = context->engine()) {
+ component.setObject(new QQmlComponent(
+ engine, context->resolvedUrl(source), mode, q), q);
+ return;
+ }
+ }
+
+ qmlWarning(q) << "createComponent: Cannot find a QML engine.";
}
#include <moc_qquickloader_p.cpp>
diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h
index 1d60f2845f..c6216ccc7c 100644
--- a/src/quick/items/qquickloader_p.h
+++ b/src/quick/items/qquickloader_p.h
@@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
+class QQmlV4Function;
class Q_QUICK_PRIVATE_EXPORT QQuickLoader : public QQuickImplicitSizeItem
{
Q_OBJECT
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index b178803c7d..dac9e2cb59 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -56,12 +56,14 @@
#include "qquickitemchangelistener_p.h"
#include <qqmlincubator.h>
-#include <private/qv4value_p.h>
+#include <private/qv4staticvalue_p.h>
+#include <private/qv4persistent_p.h>
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
+class QQmlV4Function;
class QQuickLoaderIncubator : public QQmlIncubator
{
public:
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 1f690f1e85..5d29408b06 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -331,6 +331,7 @@ bool QQuickRenderControl::initialize()
params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
params.maybeSurface = d->window;
renderContext->initialize(&params);
+ d->initialized = true;
} else {
qWarning("QRhi is only compatible with default adaptation");
return false;
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 38e46b10d4..8f16940ea3 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -81,7 +81,7 @@ public:
QQuickWindow *window() const;
protected:
- QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
+ explicit QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
Q_SIGNALS:
void renderRequested();
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index 54517e1ed6..719c682769 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -482,12 +482,12 @@ void QQuickParentChangePrivate::reverseRewindHelper(const std::unique_ptr<QQuick
{
if (!target || !snapshot)
return;
- auto targetPriv = QQuickItemPrivate::get(target);
+
// leave existing bindings alive; new bindings are applied in applyBindings
- targetPriv->x.setValueBypassingBindings(snapshot->x);
- targetPriv->y.setValueBypassingBindings(snapshot->y);
- targetPriv->width.setValueBypassingBindings(snapshot->width);
- targetPriv->height.setValueBypassingBindings(snapshot->height);
+ // setPosition and setSize update the geometry without invalidating bindings
+ target->setPosition(QPointF(snapshot->x, snapshot->y));
+ target->setSize(QSizeF(snapshot->width, snapshot->height));
+
target->setScale(snapshot->scale);
target->setRotation(snapshot->rotation);
target->setParentItem(snapshot->parent);
@@ -1094,6 +1094,7 @@ void QQuickAnchorChanges::reverse()
QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask;
QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask;
+ const QRectF oldGeometry(d->target->position(), d->target->size());
bool stateSetWidth = (stateHAnchors &&
stateHAnchors != QQuickAnchors::LeftAnchor &&
stateHAnchors != QQuickAnchors::RightAnchor &&
@@ -1102,8 +1103,11 @@ void QQuickAnchorChanges::reverse()
origHAnchors != QQuickAnchors::LeftAnchor &&
origHAnchors != QQuickAnchors::RightAnchor &&
origHAnchors != QQuickAnchors::HCenterAnchor);
- if (d->origWidth.isValid() && stateSetWidth && !origSetWidth)
- d->target->setWidth(d->origWidth.value);
+ if (d->origWidth.isValid() && stateSetWidth && !origSetWidth && !qt_is_nan(d->origWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (targetPrivate->width != d->origWidth)
+ targetPrivate->width.setValueBypassingBindings(d->origWidth);
+ }
bool stateSetHeight = (stateVAnchors &&
stateVAnchors != QQuickAnchors::TopAnchor &&
@@ -1115,14 +1119,23 @@ void QQuickAnchorChanges::reverse()
origVAnchors != QQuickAnchors::BottomAnchor &&
origVAnchors != QQuickAnchors::VCenterAnchor &&
origVAnchors != QQuickAnchors::BaselineAnchor);
- if (d->origHeight.isValid() && stateSetHeight && !origSetHeight)
- d->target->setHeight(d->origHeight.value);
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (targetPrivate->height != d->origHeight)
+ targetPrivate->height.setValueBypassingBindings(d->origHeight);
+ }
- if (stateHAnchors && !origHAnchors)
- d->target->setX(d->origX);
+ if (stateHAnchors && !origHAnchors && !qt_is_nan(d->origX) && d->origX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->origX);
- if (stateVAnchors && !origVAnchors)
- d->target->setY(d->origY);
+ if (stateVAnchors && !origVAnchors && !qt_is_nan(d->origY) && d->origY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->origY);
+
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
+ }
}
QQuickStateActionEvent::EventType QQuickAnchorChanges::type() const
@@ -1316,15 +1329,31 @@ void QQuickAnchorChanges::rewind()
return;
QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target);
+ const QRectF oldGeometry(d->target->position(), d->target->size());
+
+ // Restore previous values (but not previous bindings, i.e. anchors).
+ // Also, don't drop any new bindings.
+ if (!qt_is_nan(d->rewindX) && d->rewindX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->rewindX);
+ if (!qt_is_nan(d->rewindY) && d->rewindY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->rewindY);
+
+ if (targetPrivate->widthValid() && !qt_is_nan(d->rewindWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (d->rewindWidth != targetPrivate->width)
+ targetPrivate->width.setValueBypassingBindings(d->rewindWidth);
+ }
- //restore previous values (but not previous bindings, i.e. anchors)
- d->target->setX(d->rewindX);
- d->target->setY(d->rewindY);
- if (targetPrivate->widthValid()) {
- d->target->setWidth(d->rewindWidth);
+ if (targetPrivate->heightValid() && !qt_is_nan(d->rewindHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (d->rewindHeight != targetPrivate->height)
+ targetPrivate->height.setValueBypassingBindings(d->rewindHeight);
}
- if (targetPrivate->heightValid()) {
- d->target->setHeight(d->rewindHeight);
+
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
}
}
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 4ab8c954d1..adde42321c 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -556,8 +556,8 @@
/*!
\qmlmethod Point QtQuick::TableView::cellAtPos(point position, bool includeSpacing)
- Returns the cell at the given \a position in the view. If no cell intersects with
- \a position, the return value will be \c point(-1, -1).
+ Returns the cell at the given \a position in the view. If no \l {isRowLoaded()}{loaded}
+ cell intersects with \a position, the return value will be \c point(-1, -1).
If \a includeSpacing is set to \c true, a cell's bounding box will be considered
to include half the adjacent \l rowSpacing and \l columnSpacing on each side. The
@@ -643,6 +643,66 @@
*/
/*!
+ \qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column)
+ \since 6.4
+
+ Returns the \l QModelIndex that maps to \a row and \a column in the view.
+
+ \a row and \a column should be the row and column in the view (table row and
+ table column), and not a row and column in the model.
+
+ \sa rowAtIndex(), columnAtIndex()
+*/
+
+/*!
+ \qmlmethod QModelIndex QtQuick::TableView::modelIndex(point cell)
+ \since 6.4
+
+ Convenience function for doing:
+ \code
+ modelIndex(cell.y, cell.x)
+ \endcode
+
+ A cell is simply a \l point that combines row and column into
+ a single type. Note that \c point.x will map to the column, and
+ \c point.y will map to the row.
+*/
+
+/*!
+ \qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the row in the view that maps to \a modelIndex in the model.
+
+ \sa columnAtIndex(), modelIndex()
+*/
+
+/*!
+ \qmlmethod int QtQuick::TableView::columnAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the column in the view that maps to \a modelIndex in the model.
+
+ \sa rowAtIndex(), modelIndex()
+*/
+
+/*!
+ \qmlmethod point QtQuick::TableView::cellAtIndex(QModelIndex modelIndex)
+ \since 6.4
+
+ Returns the cell in the view that maps to \a modelIndex in the model.
+ Convenience function for doing:
+
+ \code
+ Qt.point(columnAtIndex(modelIndex), rowAtIndex(modelIndex))
+ \endcode
+
+ A cell is simply a \l point that combines row and column into
+ a single type. Note that \c point.x will map to the column, and
+ \c point.y will map to the row.
+*/
+
+/*!
\qmlattachedproperty TableView QtQuick::TableView::view
This attached property holds the view that manages the delegate instance.
@@ -688,10 +748,9 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
-static const int kEdgeIndexNotSet = -2;
-static const int kEdgeIndexAtEnd = -3;
-static const char* kRequiredProperty = "_qt_isrequiredpropery_selected";
+static const char* kRequiredProperties = "_qt_tableview_requiredpropertymask";
+static const char* kRequiredProperty_selected = "selected";
const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0);
const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0);
@@ -782,6 +841,40 @@ void QQuickTableViewPrivate::dumpTable() const
qWarning() << "Window capture saved to:" << path;
}
+void QQuickTableViewPrivate::setRequiredProperty(const char *property,
+ const QVariant &value, int serializedModelIndex, QObject *object, bool init)
+{
+ if (!qobject_cast<QQmlTableInstanceModel *>(model)) {
+ // TableView only supports using required properties when backed by
+ // a QQmlTableInstanceModel. This is almost always the case, except
+ // if you assign it an ObjectModel or a DelegateModel (which are really
+ // not supported by TableView, it expects a QAIM).
+ return;
+ }
+
+ // Attaching a property list to the delegate item is just a
+ // work-around until QMetaProperty::isRequired() works (QTBUG-98846).
+ const QString propertyName = QString::fromUtf8(property);
+
+ if (init) {
+ const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value);
+ if (wasRequired) {
+ QStringList propertyList = object->property(kRequiredProperties).toStringList();
+ object->setProperty(kRequiredProperties, propertyList << propertyName);
+ }
+ } else {
+ const QStringList propertyList = object->property(kRequiredProperties).toStringList();
+ if (!propertyList.contains(propertyName)) {
+ // We only write to properties that are required
+ return;
+ }
+ const auto metaObject = object->metaObject();
+ const int propertyIndex = metaObject->indexOfProperty(property);
+ const auto metaProperty = metaObject->property(propertyIndex);
+ metaProperty.write(object, value);
+ }
+}
+
QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const
{
return const_cast<QQuickTableView *>(q_func())->contentItem();
@@ -993,7 +1086,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
const bool outsideBottom = pos.y() >= viewportRect.bottom() - 1;
if (outsideLeft) {
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
+ const bool firstColumnLoaded = atTableEnd(Qt::LeftEdge);
const qreal remainingDist = viewportRect.left() - loadedTableOuterRect.left();
if (remainingDist > 0 || !firstColumnLoaded) {
qreal stepX = step.width();
@@ -1003,7 +1096,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
dist.setWidth(pos.x() - viewportRect.left() - 1);
}
} else if (outsideRight) {
- const bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd;
+ const bool lastColumnLoaded = atTableEnd(Qt::RightEdge);
const qreal remainingDist = loadedTableOuterRect.right() - viewportRect.right();
if (remainingDist > 0 || !lastColumnLoaded) {
qreal stepX = step.width();
@@ -1015,7 +1108,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
}
if (outsideTop) {
- const bool firstRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
+ const bool firstRowLoaded = atTableEnd(Qt::TopEdge);
const qreal remainingDist = viewportRect.top() - loadedTableOuterRect.top();
if (remainingDist > 0 || !firstRowLoaded) {
qreal stepY = step.height();
@@ -1025,7 +1118,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
dist.setHeight(pos.y() - viewportRect.top() - 1);
}
} else if (outsideBottom) {
- const bool lastRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd;
+ const bool lastRowLoaded = atTableEnd(Qt::BottomEdge);
const qreal remainingDist = loadedTableOuterRect.bottom() - viewportRect.bottom();
if (remainingDist > 0 || !lastRowLoaded) {
qreal stepY = step.height();
@@ -1081,14 +1174,13 @@ int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex)
{
// Convert QModelIndex to cell index. A cell index is just an
// integer representation of a cell instead of using a QPoint.
- if (modelIndex.parent().isValid()) {
- // TableView only uses the root items of the model
+ const QPoint cell = q_func()->cellAtIndex(modelIndex);
+ if (!cellIsValid(cell))
return -1;
- }
- return modelIndexAtCell(QPoint(modelIndex.column(), modelIndex.row()));
+ return modelIndexAtCell(cell);
}
-int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge)
+int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) const
{
return int(log2(float(edge)));
}
@@ -1102,7 +1194,7 @@ void QQuickTableViewPrivate::clearEdgeSizeCache()
cachedNextVisibleEdgeIndex[edgeToArrayIndex(edge)].startIndex = kEdgeIndexNotSet;
}
-int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge)
+int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const
{
// Find the next column (or row) around the loaded table that is
// visible, and should be loaded next if the content item moves.
@@ -1117,7 +1209,7 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge)
return nextVisibleEdgeIndex(edge, startIndex);
}
-int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex)
+int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const
{
// First check if we have already searched for the first visible index
// after the given startIndex recently, and if so, return the cached result.
@@ -1200,28 +1292,6 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex)
return foundIndex;
}
-bool QQuickTableViewPrivate::allColumnsLoaded()
-{
- // Returns true if all the columns in the model (that are not
- // hidden by the columnWidthProvider) are currently loaded and visible.
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
- if (!firstColumnLoaded)
- return false;
- bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd;
- return lastColumnLoaded;
-}
-
-bool QQuickTableViewPrivate::allRowsLoaded()
-{
- // Returns true if all the rows in the model (that are not hidden
- // by the columnWidthProvider) are currently loaded and visible.
- const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
- if (!firstColumnLoaded)
- return false;
- bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd;
- return lastColumnLoaded;
-}
-
void QQuickTableViewPrivate::updateContentWidth()
{
// Note that we actually never really know what the content size / size of the full table will
@@ -1941,6 +2011,13 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column)
return columnWidth;
}
+qreal QQuickTableViewPrivate::getEffectiveRowY(int row) const
+{
+ // Return y pos of row after layout
+ Q_TABLEVIEW_ASSERT(loadedRows.contains(row), row);
+ return loadedTableItem(QPoint(leftColumn(), row))->geometry().y();
+}
+
qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const
{
// Return row height after layout
@@ -1948,6 +2025,13 @@ qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const
return loadedTableItem(QPoint(leftColumn(), row))->geometry().height();
}
+qreal QQuickTableViewPrivate::getEffectiveColumnX(int column) const
+{
+ // Return x pos of column after layout
+ Q_TABLEVIEW_ASSERT(loadedColumns.contains(column), column);
+ return loadedTableItem(QPoint(column, topRow()))->geometry().x();
+}
+
qreal QQuickTableViewPrivate::getEffectiveColumnWidth(int column) const
{
// Return column width after layout
@@ -1989,7 +2073,7 @@ qreal QQuickTableViewPrivate::getRowLayoutHeight(int row)
return rowHeight;
}
-qreal QQuickTableViewPrivate::getColumnWidth(int column)
+qreal QQuickTableViewPrivate::getColumnWidth(int column) const
{
// Return the width of the given column, if explicitly set. Return 0 if the column
// is hidden, and -1 if the width is not set (which means that the width should
@@ -2030,7 +2114,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column)
return columnWidth;
}
-qreal QQuickTableViewPrivate::getRowHeight(int row)
+qreal QQuickTableViewPrivate::getRowHeight(int row) const
{
// Return the height of the given row, if explicitly set. Return 0 if the row
// is hidden, and -1 if the height is not set (which means that the height should
@@ -2071,14 +2155,14 @@ qreal QQuickTableViewPrivate::getRowHeight(int row)
return rowHeight;
}
-bool QQuickTableViewPrivate::isColumnHidden(int column)
+bool QQuickTableViewPrivate::isColumnHidden(int column) const
{
// A column is hidden if the width is explicit set to zero (either by
// using a columnWidthProvider, or by overriding getColumnWidth()).
return qFuzzyIsNull(getColumnWidth(column));
}
-bool QQuickTableViewPrivate::isRowHidden(int row)
+bool QQuickTableViewPrivate::isRowHidden(int row) const
{
// A row is hidden if the height is explicit set to zero (either by
// using a rowHeightProvider, or by overriding getRowHeight()).
@@ -2336,18 +2420,25 @@ void QQuickTableViewPrivate::processRebuildTable()
return;
}
+ if (rebuildState == RebuildState::CancelOvershoot) {
+ cancelOvershootAfterLayout();
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
const bool preload = (rebuildOptions & RebuildOption::All
&& reusableFlag == QQmlTableInstanceModel::Reusable);
if (rebuildState == RebuildState::PreloadColumns) {
- if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) != kEdgeIndexAtEnd)
+ if (preload && !atTableEnd(Qt::RightEdge))
loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
if (!moveToNextRebuildState())
return;
}
if (rebuildState == RebuildState::PreloadRows) {
- if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) != kEdgeIndexAtEnd)
+ if (preload && !atTableEnd(Qt::BottomEdge))
loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
if (!moveToNextRebuildState())
return;
@@ -2585,12 +2676,14 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
relayoutTableItems();
syncLoadedTableRectFromLoadedTable();
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) {
+ const bool allColumnsLoaded = atTableEnd(Qt::LeftEdge) && atTableEnd(Qt::RightEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded) {
updateAverageColumnWidth();
updateContentWidth();
}
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) {
+ const bool allRowsLoaded = atTableEnd(Qt::TopEdge) && atTableEnd(Qt::BottomEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded) {
updateAverageRowHeight();
updateContentHeight();
}
@@ -2670,6 +2763,32 @@ void QQuickTableViewPrivate::adjustViewportYAccordingToAlignment()
syncViewportRect();
}
+void QQuickTableViewPrivate::cancelOvershootAfterLayout()
+{
+ Q_Q(QQuickTableView);
+
+ // Note: we only want to cancel overshoot from a rebuild if we're supposed to position
+ // the view on a specific cell. The app is allowed to overshoot by setting contentX and
+ // contentY manually. Also, if this view is a sync child, we should always stay in sync
+ // with the syncView, so then we don't do anything.
+ const bool positionVertically = rebuildOptions.testFlag(RebuildOption::PositionViewAtRow);
+ const bool positionHorizontally = rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn);
+ const bool cancelVertically = positionVertically && !syncVertically;
+ const bool cancelHorizontally = positionHorizontally && !syncHorizontally;
+
+ if (cancelHorizontally && !qFuzzyIsNull(q->horizontalOvershoot())) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot horizontally:" << q->horizontalOvershoot();
+ setLocalViewportX(q->horizontalOvershoot() < 0 ? -q->minXExtent() : -q->maxXExtent());
+ syncViewportRect();
+ }
+
+ if (cancelVertically && !qFuzzyIsNull(q->verticalOvershoot())) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot vertically:" << q->verticalOvershoot();
+ setLocalViewportY(q->verticalOvershoot() < 0 ? -q->minYExtent() : -q->maxYExtent());
+ syncViewportRect();
+ }
+}
+
void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
{
Q_Q(QQuickTableView);
@@ -2728,7 +2847,7 @@ void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMo
processLoadRequest();
}
-void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
+void QQuickTableViewPrivate::loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode)
{
// Unload table edges that have been moved outside the visible part of the
// table (including buffer area), and load new edges that has been moved inside.
@@ -2765,7 +2884,7 @@ void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) {
tableModified = true;
- loadEdge(edge, QQmlIncubator::AsynchronousIfNested);
+ loadEdge(edge, incubationMode);
if (loadRequest.isActive())
return;
}
@@ -2970,11 +3089,10 @@ bool QQuickTableViewPrivate::selectedInSelectionModel(const QPoint &cell) const
if (!model)
return false;
- const QModelIndex modelIndex = model->index(cell.y(), cell.x());
- return selectionModel->isSelected(modelIndex);
+ return selectionModel->isSelected(q_func()->modelIndex(cell));
}
-void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const
+void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
const auto &selectedIndexes = selected.indexes();
const auto &deselectedIndexes = deselected.indexes();
@@ -2984,40 +3102,25 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti
setSelectedOnDelegateItem(deselectedIndexes.at(i), false);
}
-void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems() const
-{
- for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) {
- const QPoint cell = cellAtModelIndex(*it);
- const bool selected = selectedInSelectionModel(cell);
- setSelectedOnDelegateItem(loadedTableItem(cell)->item, selected);
- }
-}
-
-void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const
+void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select)
{
const int cellIndex = modelIndexToCellIndex(modelIndex);
if (!loadedItems.contains(cellIndex))
return;
const QPoint cell = cellAtModelIndex(cellIndex);
- setSelectedOnDelegateItem(loadedTableItem(cell)->item, select);
+ QQuickItem *item = loadedTableItem(cell)->item;
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false);
}
-void QQuickTableViewPrivate::setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const
+void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems()
{
- if (!delegateItem->property(kRequiredProperty).toBool()) {
- // We only assign to "selected" if it's a required property. Otherwise
- // we assume (for backwards compatibility) that the property is used
- // by the delegate for something else.
- // Note: kRequiredProperty is a work-around until QMetaProperty::isRequired() works.
- return;
+ for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) {
+ const int cellIndex = *it;
+ const QPoint cell = cellAtModelIndex(cellIndex);
+ const bool selected = selectedInSelectionModel(cell);
+ QQuickItem *item = loadedTableItem(cell)->item;
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), cellIndex, item, false);
}
-
- // Note that several delegates might be in use (in case of a DelegateChooser), and
- // the delegate can also change. So we cannot cache the propertyIndex.
- const auto metaObject = delegateItem->metaObject();
- const int propertyIndex = metaObject->indexOfProperty("selected");
- const auto metaProperty = metaObject->property(propertyIndex);
- metaProperty.write(delegateItem, QVariant::fromValue(select));
}
void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*)
@@ -3048,14 +3151,7 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
const QPoint cell = cellAtModelIndex(modelIndex);
const bool selected = selectedInSelectionModel(cell);
-
- if (qobject_cast<QQmlTableInstanceModel *>(model)) {
- const bool wasRequired = model->setRequiredProperty(modelIndex, QStringLiteral("selected"), selected);
- if (wasRequired) {
- // Work-around until QMetaProperty::isRequired() works
- item->setProperty(kRequiredProperty, true);
- }
- }
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, true);
if (auto attached = getAttachedObject(object))
attached->setView(q);
@@ -3071,10 +3167,9 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
{
- auto item = static_cast<QQuickItem*>(object);
const QPoint cell = cellAtModelIndex(modelIndex);
const bool selected = selectedInSelectionModel(cell);
- setSelectedOnDelegateItem(item, selected);
+ setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, false);
if (auto attached = getAttachedObject(object))
emit attached->reused();
@@ -3886,27 +3981,29 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing)
{
Q_D(const QQuickTableView);
- if (!boundingRect().contains(position))
+ const QPointF localPos = mapToItem(d->contentItem, position);
+ if (!d->loadedTableOuterRect.contains(localPos))
return QPoint(-1, -1);
const qreal hSpace = d->cellSpacing.width();
const qreal vSpace = d->cellSpacing.height();
- qreal currentColumnEnd = d->loadedTableOuterRect.x() - contentX();
- qreal currentRowEnd = d->loadedTableOuterRect.y() - contentY();
+ qreal currentColumnEnd = d->loadedTableOuterRect.x();
+ qreal currentRowEnd = d->loadedTableOuterRect.y();
+
int foundColumn = -1;
int foundRow = -1;
for (const int column : d->loadedColumns) {
currentColumnEnd += d->getEffectiveColumnWidth(column);
- if (position.x() < currentColumnEnd) {
+ if (localPos.x() < currentColumnEnd) {
foundColumn = column;
break;
}
currentColumnEnd += hSpace;
- if (!includeSpacing && position.x() < currentColumnEnd) {
+ if (!includeSpacing && localPos.x() < currentColumnEnd) {
// Hit spacing
return QPoint(-1, -1);
- } else if (includeSpacing && position.x() < currentColumnEnd - (hSpace / 2)) {
+ } else if (includeSpacing && localPos.x() < currentColumnEnd - (hSpace / 2)) {
foundColumn = column;
break;
}
@@ -3914,16 +4011,16 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing)
for (const int row : d->loadedRows) {
currentRowEnd += d->getEffectiveRowHeight(row);
- if (position.y() < currentRowEnd) {
+ if (localPos.y() < currentRowEnd) {
foundRow = row;
break;
}
currentRowEnd += vSpace;
- if (!includeSpacing && position.y() < currentRowEnd) {
+ if (!includeSpacing && localPos.y() < currentRowEnd) {
// Hit spacing
return QPoint(-1, -1);
}
- if (includeSpacing && position.y() < currentRowEnd - (vSpace / 2)) {
+ if (includeSpacing && localPos.y() < currentRowEnd - (vSpace / 2)) {
foundRow = row;
break;
}
@@ -4000,6 +4097,41 @@ qreal QQuickTableView::implicitRowHeight(int row) const
return d->sizeHintForRow(row);
}
+QModelIndex QQuickTableView::modelIndex(const QPoint &cell) const
+{
+ Q_D(const QQuickTableView);
+ if (cell.x() < 0 || cell.x() >= columns() || cell.y() < 0 || cell.y() >= rows())
+ return {};
+
+ auto const qaim = d->model->abstractItemModel();
+ if (!qaim)
+ return {};
+
+ return qaim->index(cell.y(), cell.x());
+}
+
+QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const
+{
+ if (!index.isValid() || index.parent().isValid())
+ return {-1, -1};
+ return {index.column(), index.row()};
+}
+
+QModelIndex QQuickTableView::modelIndex(int row, int column) const
+{
+ return modelIndex({column, row});
+}
+
+int QQuickTableView::rowAtIndex(const QModelIndex &index) const
+{
+ return cellAtIndex(index).y();
+}
+
+int QQuickTableView::columnAtIndex(const QModelIndex &index) const
+{
+ return cellAtIndex(index).x();
+}
+
void QQuickTableView::forceLayout()
{
d_func()->forceLayout();
@@ -4092,9 +4224,9 @@ void QQuickTableSectionSizeProvider::setSize(int section, qreal size)
}
// return -1.0 if no valid explicit size retrieved
-qreal QQuickTableSectionSizeProvider::size(int section)
+qreal QQuickTableSectionSizeProvider::size(int section) const
{
- Q_D(QQuickTableSectionSizeProvider);
+ Q_D(const QQuickTableSectionSizeProvider);
auto it = d->hash.find(section);
if (it != d->hash.end())
return *it;
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index 40a0fae7c1..830bef6c68 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -156,6 +156,12 @@ public:
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitColumnWidth(int column) const;
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitRowHeight(int row) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(const QPoint &cell) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QPoint cellAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
+ Q_REVISION(6, 4) Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
+
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
Q_SIGNALS:
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index bbab9f6a25..5bbdd07528 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -71,6 +71,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
+static const int kEdgeIndexNotSet = -2;
+static const int kEdgeIndexAtEnd = -3;
class FxTableItem;
class QQuickTableSectionSizeProviderPrivate;
@@ -81,7 +83,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject {
public:
QQuickTableSectionSizeProvider(QObject *parent=nullptr);
void setSize(int section, qreal size);
- qreal size(int section);
+ qreal size(int section) const;
bool resetSize(int section);
void resetAll();
@@ -202,6 +204,7 @@ public:
VerifyTable,
LayoutTable,
LoadAndUnloadAfterLayout,
+ CancelOvershoot,
PreloadColumns,
PreloadRows,
MovePreloadedItemsToPool,
@@ -276,7 +279,7 @@ public:
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool layoutWarningIssued = false;
+ mutable bool layoutWarningIssued = false;
bool polishing = false;
bool syncVertically = false;
bool syncHorizontally = false;
@@ -295,9 +298,9 @@ public:
QQuickTableSectionSizeProvider rowHeights;
QQuickTableSectionSizeProvider columnWidths;
- EdgeRange cachedNextVisibleEdgeIndex[4];
- EdgeRange cachedColumnWidth;
- EdgeRange cachedRowHeight;
+ mutable EdgeRange cachedNextVisibleEdgeIndex[4];
+ mutable EdgeRange cachedColumnWidth;
+ mutable EdgeRange cachedRowHeight;
// TableView uses contentWidth/height to report the size of the table (this
// will e.g make scrollbars written for Flickable work out of the box). This
@@ -358,14 +361,16 @@ public:
QSize calculateTableSize();
void updateTableSize();
- inline bool isColumnHidden(int column);
- inline bool isRowHidden(int row);
+ inline bool isColumnHidden(int column) const;
+ inline bool isRowHidden(int row) const;
qreal getColumnLayoutWidth(int column);
qreal getRowLayoutHeight(int row);
- qreal getColumnWidth(int column);
- qreal getRowHeight(int row);
+ qreal getColumnWidth(int column) const;
+ qreal getRowHeight(int row) const;
+ qreal getEffectiveRowY(int row) const;
qreal getEffectiveRowHeight(int row) const;
+ qreal getEffectiveColumnX(int column) const;
qreal getEffectiveColumnWidth(int column) const;
int topRow() const { return *loadedRows.cbegin(); }
@@ -396,11 +401,11 @@ public:
void syncLoadedTableFromLoadRequest();
void shiftLoadedTableRect(const QPointF newPosition);
- int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
- int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
- bool allColumnsLoaded();
- bool allRowsLoaded();
- inline int edgeToArrayIndex(Qt::Edge edge);
+ int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const;
+ int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const;
+ inline bool atTableEnd(Qt::Edge edge) const { return nextVisibleEdgeIndexAroundLoadedTable(edge) == kEdgeIndexAtEnd; }
+ inline bool atTableEnd(Qt::Edge edge, int startIndex) const { return nextVisibleEdgeIndex(edge, startIndex) == kEdgeIndexAtEnd; }
+ inline int edgeToArrayIndex(Qt::Edge edge) const;
void clearEdgeSizeCache();
bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
@@ -421,7 +426,7 @@ public:
void unloadItem(const QPoint &cell);
void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
void unloadEdge(Qt::Edge edge);
- void loadAndUnloadVisibleEdges();
+ void loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested);
void drainReusePoolAfterLoadRequest();
void processLoadRequest();
@@ -433,6 +438,7 @@ public:
void layoutAfterLoadingInitialTable();
void adjustViewportXAccordingToAlignment();
void adjustViewportYAccordingToAlignment();
+ void cancelOvershootAfterLayout();
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
@@ -473,10 +479,9 @@ public:
void syncViewportPosRecursive();
bool selectedInSelectionModel(const QPoint &cell) const;
- void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const;
- void updateSelectedOnAllDelegateItems() const;
- void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const;
- void setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const;
+ void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected);
+ void updateSelectedOnAllDelegateItems();
+ void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select);
void fetchMoreData();
@@ -486,6 +491,11 @@ public:
inline QString tableLayoutToString() const;
void dumpTable() const;
+ void setRequiredProperty(const char *property,
+ const QVariant &value,
+ int serializedModelIndex,
+ QObject *object, bool init);
+
// QQuickSelectable
QQuickItem *selectionPointerHandlerTarget() const override;
void setSelectionStartPos(const QPointF &pos) override;
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index f5dca6e0f5..66baa7ed7c 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -772,11 +772,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
const bool pixelSize = font.pixelSize() != -1;
QString layoutText = layout.text();
- int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
- int smallFont = fontSizeMode() != QQuickText::FixedSize
- ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
- : largeFont;
- int scaledFontSize = largeFont;
+ const qreal minimumSize = pixelSize
+ ? static_cast<qreal>(minimumPixelSize())
+ : minimumPointSize();
+ qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
+ qreal smallFont = fontSizeMode() != QQuickText::FixedSize
+ ? qMin<qreal>(minimumSize, largeFont)
+ : largeFont;
+ qreal scaledFontSize = largeFont;
+ const qreal sizeFittingThreshold(0.01);
bool widthChanged = false;
widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit);
@@ -805,7 +809,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (pixelSize)
scaledFont.setPixelSize(scaledFontSize);
else
- scaledFont.setPointSize(scaledFontSize);
+ scaledFont.setPointSizeF(scaledFontSize);
if (layout.font() != scaledFont)
layout.setFont(scaledFont);
}
@@ -1069,40 +1073,45 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (!horizontalFit && !verticalFit)
break;
+ // Can't find a better fit
+ if (qFuzzyCompare(smallFont, largeFont))
+ break;
+
// Try and find a font size that better fits the dimensions of the element.
if (horizontalFit) {
if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
widthExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
- if (pixelSize)
- scaledFont.setPixelSize(scaledFontSize);
- else
- scaledFont.setPointSize(scaledFontSize);
+
continue;
} else if (!verticalFit) {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
if (verticalFit) {
if (truncateHeight || unelidedRect.height() > maxHeight) {
heightExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
} else {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
}
diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp
index 271df508de..e6cf4a8305 100644
--- a/src/quick/items/qquicktreeview.cpp
+++ b/src/quick/items/qquicktreeview.cpp
@@ -149,126 +149,129 @@
\note this function will not affect the model, only
the visual representation in the view.
- \sa collapse(), isExpanded()
+ \sa collapse(), isExpanded(), expandRecursively()
*/
/*!
- \qmlmethod QtQuick::TreeView::collapse(row)
+ \qmlmethod QtQuick::TreeView::expandRecursively(row = -1, depth = -1)
+ \since 6.4
- Collapses the tree node at the given \a row in the view.
+ Expands the tree node at the given \a row in the view recursively down to
+ \a depth. \a depth should be relative to the depth of \a row. If
+ \a depth is \c -1, the tree will be expanded all the way down to all leaves.
+
+ For a model that has more than one root, you can also call this function
+ with \a row equal to \c -1. This will expand all roots. Hence, calling
+ expandRecursively(-1, -1), or simply expandRecursively(), will expand
+ all nodes in the model.
\a row should be the row in the view (table row), and not a row in the model.
- \note this function will not affect the model, only
- the visual representation in the view.
+ \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more} data.
+ \note This function will not affect the model, only the visual representation in the view.
+ \warning If the model contains a large number of items, this function will
+ take some time to execute.
- \sa expand(), isExpanded()
+ \sa collapseRecursively(), expand(), collapse(), isExpanded(), depth()
*/
/*!
- \qmlmethod QtQuick::TreeView::toggleExpanded(row)
+ \qmlmethod QtQuick::TreeView::expandToIndex(QModelIndex index)
+ \since 6.4
- Toggles if the tree node at the given \a row should be expanded.
- This is a convenience for doing:
+ Expands the tree from the given model \a index, and recursively all the way up
+ to the root. The result will be that the delegate item that represents \a index
+ becomes visible in the view (unless it ends up outside the viewport). To
+ ensure that the row ends up visible in the viewport, you can do:
\code
- if (isExpanded(row))
- collapse(row)
- else
- expand(row)
+ expandToIndex(index)
+ forceLayout()
+ positionViewAtRow(rowAtIndex(index), Qt.AlignVCenter)
\endcode
- \a row should be the row in the view (table row), and not a row in the model.
+ \sa expand(), expandRecursively()
*/
/*!
- \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(row, column)
-
- Returns the \l QModelIndex that maps to \a row and \a column in the view.
-
- \a row and \a column should be the row and column in the view (table row and
- table column), and not a row and column in the model.
-
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- index in the tree model maps to the given row and column in the view.
+ \qmlmethod QtQuick::TreeView::collapse(row)
- \sa rowAtIndex(), columnAtIndex()
-*/
+ Collapses the tree node at the given \a row in the view.
-/*!
- \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(point cell)
+ \a row should be the row in the view (table row), and not a row in the model.
- Convenience function for doing:
- \code
- modelIndex(cell.y, cell.x)
- \endcode
+ \note this function will not affect the model, only
+ the visual representation in the view.
- A cell is simply a \l point that combines row and column into
- a single type. Note that \c point.x will map to the column, and
- \c point.y will map to the row.
+ \sa expand(), isExpanded()
*/
/*!
- \qmlmethod int QtQuick::TreeView::rowAtIndex(modelIndex)
-
- Returns the row in the view that maps to \a modelIndex in the model.
+ \qmlmethod QtQuick::TreeView::collapseRecursively(row = -1)
+ \since 6.4
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- row in the view the given model index maps to.
+ Collapses the tree node at the given \a row in the view recursively down to
+ all leaves.
- \note \a modelIndex must be a \l QModelIndex.
+ For a model has more than one root, you can also call this function
+ with \a row equal to \c -1. This will collapse all roots. Hence, calling
+ collapseRecursively(-1), or simply collapseRecursively(), will collapse
+ all nodes in the model.
- \sa columnAtIndex(), modelIndex()
-*/
-
-/*!
- \qmlmethod int QtQuick::TreeView::columnAtIndex(modelIndex)
-
- Returns the column in the view that maps to \a modelIndex in the model.
-
- The assigned model, which is a tree model, is converted to a flat table
- model internally so that it can be shown in a TableView (which TreeView
- inherits). This function can be used whenever you need to know which
- column in the view the given model index maps to.
+ \a row should be the row in the view (table row), and not a row in the model.
- \note \a modelIndex must be a \l QModelIndex.
+ \note this function will not affect the model, only
+ the visual representation in the view.
- \sa rowAtIndex(), modelIndex()
+ \sa expandRecursively(), expand(), collapse(), isExpanded(), depth()
*/
/*!
- \qmlmethod point QtQuick::TreeView::cellAtIndex(modelIndex)
+ \qmlmethod QtQuick::TreeView::toggleExpanded(row)
- Convenience function for doing:
+ Toggles if the tree node at the given \a row should be expanded.
+ This is a convenience for doing:
- \c {Qt.point(columnAtIndex(}\a {modelIndex}\c{), rowAtIndex(}\a {modelIndex}\c{))}
+ \code
+ if (isExpanded(row))
+ collapse(row)
+ else
+ expand(row)
+ \endcode
- A cell is simply a \l point that combines row and column into
- a single type. Note that \c point.x will map to the column, and
- \c point.y will map to the row.
+ \a row should be the row in the view (table row), and not a row in the model.
*/
/*!
- \qmlsignal QtQuick::TreeView::expanded(row)
+ \qmlsignal QtQuick::TreeView::expanded(row, depth)
This signal is emitted when a \a row is expanded in the view.
+ \a row and \a depth will be equal to the arguments given to the call
+ that caused the expansion to happen (\l expand() or \l expandRecursively()).
+ In case of \l expand(), \a depth will always be \c 1.
+ In case of \l expandToIndex(), \a depth will be the depth of the
+ target index.
+
+ \note when a row is expanded recursively, the expanded signal will
+ only be emitted for that one row, and not for its descendants.
\sa collapsed(), expand(), collapse(), toggleExpanded()
*/
/*!
- \qmlsignal QtQuick::TreeView::collapsed(row)
+ \qmlsignal QtQuick::TreeView::collapsed(row, recursively)
This signal is emitted when a \a row is collapsed in the view.
+ \a row will be equal to the argument given to the call that caused
+ the collapse to happen (\l collapse() or \l collapseRecursively()).
+ If the row was collapsed recursively, \a recursively will be \c true.
+
+ \note when a row is collapsed recursively, the collapsed signal will
+ only be emitted for that one row, and not for its descendants.
\sa expanded(), expand(), collapse(), toggleExpanded()
*/
-static const char* kRequiredProperties = "_qt_treeview_requiredpropertymask";
// Hard-code the tree column to be 0 for now
static const int kTreeColumn = 0;
@@ -343,32 +346,6 @@ void QQuickTreeViewPrivate::dataChangedCallback(
}
}
-void QQuickTreeViewPrivate::setRequiredProperty(const char *property,
- const QVariant &value, int serializedModelIndex, QObject *object, bool init)
-{
- // Attaching a property list to the delegate item is just a
- // work-around until QMetaProperty::isRequired() works!
- const QString propertyName = QString::fromUtf8(property);
-
- if (init) {
- const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value);
- if (wasRequired) {
- QStringList propertyList = object->property(kRequiredProperties).toStringList();
- object->setProperty(kRequiredProperties, propertyList << propertyName);
- }
- } else {
- const QStringList propertyList = object->property(kRequiredProperties).toStringList();
- if (!propertyList.contains(propertyName)) {
- // We only write to properties that are required
- return;
- }
- const auto metaObject = object->metaObject();
- const int propertyIndex = metaObject->indexOfProperty(property);
- const auto metaProperty = metaObject->property(propertyIndex);
- metaProperty.write(object, value);
- }
-}
-
void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject *object, bool init)
{
Q_Q(QQuickTreeView);
@@ -420,22 +397,97 @@ bool QQuickTreeView::isExpanded(int row) const
void QQuickTreeView::expand(int row)
{
+ if (row >= 0)
+ expandRecursively(row, 1);
+}
+
+void QQuickTreeView::expandRecursively(int row, int depth)
+{
Q_D(QQuickTreeView);
- if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
+ if (row >= d->m_treeModelToTableModel.rowCount())
+ return;
+ if (row < 0 && row != -1)
+ return;
+ if (depth == 0 || depth < -1)
+ return;
+
+ auto expandRowRecursively = [=](int startRow) {
+ d->m_treeModelToTableModel.expandRecursively(startRow, depth);
+ // Update the expanded state of the startRow. The descendant rows that gets
+ // expanded will get the correct state set from initItem/itemReused instead.
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, startRow);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+ };
+
+ if (row >= 0) {
+ // Expand only one row recursively
+ const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
+ if (isExpanded && depth == 1)
+ return;
+ expandRowRecursively(row);
+ } else {
+ // Expand all root nodes recursively
+ const auto model = d->m_treeModelToTableModel.model();
+ for (int r = 0; r < model->rowCount(); ++r) {
+ const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
+ if (rootRow != -1)
+ expandRowRecursively(rootRow);
+ }
+ }
+
+ emit expanded(row, depth);
+}
+
+void QQuickTreeView::expandToIndex(const QModelIndex &index)
+{
+ Q_D(QQuickTreeView);
+
+ if (!index.isValid()) {
+ qmlWarning(this) << "index is not valid: " << index;
return;
+ }
- if (d->m_treeModelToTableModel.isExpanded(row))
+ if (index.model() != d->m_treeModelToTableModel.model()) {
+ qmlWarning(this) << "index doesn't belong to correct model: " << index;
return;
+ }
- d->m_treeModelToTableModel.expandRow(row);
+ if (rowAtIndex(index) != -1) {
+ // index is already visible
+ return;
+ }
- for (int c = leftColumn(); c <= rightColumn(); ++c) {
- const QPoint treeNodeCell(c, row);
- if (const auto item = itemAtCell(treeNodeCell))
- d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ int depth = 1;
+ QModelIndex parent = index.parent();
+ int row = rowAtIndex(parent);
+
+ while (parent.isValid()) {
+ if (row != -1) {
+ // The node is already visible, since it maps to a row in the table!
+ d->m_treeModelToTableModel.expandRow(row);
+
+ // Update the state of the already existing delegate item
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, row);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+
+ // When we hit a node that is visible, we know that all other nodes
+ // up to the parent have to be visible as well, so we can stop.
+ break;
+ } else {
+ d->m_treeModelToTableModel.expand(parent);
+ parent = parent.parent();
+ row = rowAtIndex(parent);
+ depth++;
+ }
}
- emit expanded(row);
+ emit expanded(row, depth);
}
void QQuickTreeView::collapse(int row)
@@ -455,7 +507,42 @@ void QQuickTreeView::collapse(int row)
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
}
- emit collapsed(row);
+ emit collapsed(row, false);
+}
+
+void QQuickTreeView::collapseRecursively(int row)
+{
+ Q_D(QQuickTreeView);
+ if (row >= d->m_treeModelToTableModel.rowCount())
+ return;
+ if (row < 0 && row != -1)
+ return;
+
+ auto collapseRowRecursive = [=](int startRow) {
+ // Always collapse descendants recursively,
+ // even if the top row itself is already collapsed.
+ d->m_treeModelToTableModel.collapseRecursively(startRow);
+ // Update the expanded state of the (still visible) startRow
+ for (int c = leftColumn(); c <= rightColumn(); ++c) {
+ const QPoint treeNodeCell(c, startRow);
+ if (const auto item = itemAtCell(treeNodeCell))
+ d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
+ }
+ };
+
+ if (row >= 0) {
+ collapseRowRecursive(row);
+ } else {
+ // Collapse all root nodes recursively
+ const auto model = d->m_treeModelToTableModel.model();
+ for (int r = 0; r < model->rowCount(); ++r) {
+ const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
+ if (rootRow != -1)
+ collapseRowRecursive(rootRow);
+ }
+ }
+
+ emit collapsed(row, true);
}
void QQuickTreeView::toggleExpanded(int row)
@@ -466,32 +553,22 @@ void QQuickTreeView::toggleExpanded(int row)
expand(row);
}
-QModelIndex QQuickTreeView::modelIndex(int row, int column) const
+QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const
{
Q_D(const QQuickTreeView);
- const QModelIndex tableIndex = d->m_treeModelToTableModel.index(row, column);
+ const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
return d->m_treeModelToTableModel.mapToModel(tableIndex);
}
-QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const
-{
- return modelIndex(cell.y(), cell.x());
-}
-
-int QQuickTreeView::rowAtIndex(const QModelIndex &index) const
-{
- return d_func()->m_treeModelToTableModel.mapFromModel(index).row();
-}
-
-int QQuickTreeView::columnAtIndex(const QModelIndex &index) const
-{
- return d_func()->m_treeModelToTableModel.mapFromModel(index).column();
-}
-
QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const
{
const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
return QPoint(tableIndex.column(), tableIndex.row());
}
+QModelIndex QQuickTreeView::modelIndex(int row, int column) const
+{
+ return modelIndex({column, row});
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h
index c3e4b5a38f..870b29d329 100644
--- a/src/quick/items/qquicktreeview_p.h
+++ b/src/quick/items/qquicktreeview_p.h
@@ -75,15 +75,17 @@ public:
Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void toggleExpanded(int row);
- Q_INVOKABLE QModelIndex modelIndex(int row, int column) const;
- Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const;
- Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
- Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
- Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const;
+ Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
+ Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
+ Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index);
+
+ Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override;
+ Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override;
+ Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
signals:
- void expanded(int row);
- void collapsed(int row);
+ void expanded(int row, int depth);
+ void collapsed(int row, bool recursively);
private:
Q_DISABLE_COPY(QQuickTreeView)
diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h
index db974aefc9..47d87d2ac1 100644
--- a/src/quick/items/qquicktreeview_p_p.h
+++ b/src/quick/items/qquicktreeview_p_p.h
@@ -77,7 +77,6 @@ public:
const QModelIndex &bottomRight,
const QVector<int> &roles);
- void setRequiredProperty(const char *property, const QVariant &value, int serializedModelIndex, QObject *object, bool init);
void updateRequiredProperties(int serializedModelIndex, QObject *object, bool init);
public:
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 1bc266bbbf..7018a84395 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -60,17 +60,12 @@
#include <QtCore/QWeakPointer>
#include <QtQml/qqmlengine.h>
-#include <private/qv4object_p.h>
#include "qquickwindow_p.h"
#include "qquickitemchangelistener_p.h"
QT_BEGIN_NAMESPACE
-namespace QV4 {
-struct ExecutionEngine;
-}
-
class QQmlContext;
class QQmlError;
class QQuickItem;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 156ecdaadb..f68848444e 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -428,10 +428,10 @@ static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio)
void QQuickWindow::physicalDpiChanged()
{
Q_D(QQuickWindow);
- const qreal newPixelRatio = screen()->devicePixelRatio();
- if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio))
+ const qreal newPixelRatio = effectiveDevicePixelRatio();
+ if (qFuzzyCompare(newPixelRatio, d->lastReportedItemDevicePixelRatio))
return;
- d->devicePixelRatio = newPixelRatio;
+ d->lastReportedItemDevicePixelRatio = newPixelRatio;
if (d->contentItem)
updatePixelRatioHelper(d->contentItem, newPixelRatio);
}
@@ -514,7 +514,6 @@ void QQuickWindowPrivate::ensureCustomRenderTarget()
redirect.renderTargetDirty = false;
redirect.rt.reset(rhi);
- redirect.devicePixelRatio = customRenderTarget.devicePixelRatio();
// a default constructed QQuickRenderTarget means no redirection
if (customRenderTarget.isNull())
@@ -535,11 +534,6 @@ void QQuickWindowPrivate::syncSceneGraph()
ensureCustomRenderTarget();
- // Calculate the dpr the same way renderSceneGraph() will.
- qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (redirect.rt.renderTarget && !QQuickRenderControl::renderWindowFor(q))
- devicePixelRatio = redirect.devicePixelRatio;
-
QRhiCommandBuffer *cb = nullptr;
if (rhi) {
if (redirect.commandBuffer)
@@ -547,7 +541,7 @@ void QQuickWindowPrivate::syncSceneGraph()
else
cb = swapchain->currentFrameCommandBuffer();
}
- context->prepareSync(devicePixelRatio, cb, graphicsConfig);
+ context->prepareSync(q->effectiveDevicePixelRatio(), cb, graphicsConfig);
animationController->beforeNodeSync();
@@ -646,36 +640,21 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
if (flipY)
matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (redirect.rt.renderTarget) {
- const QSize pixelSize = redirect.rt.renderTarget->pixelSize();
- QRect rect(QPoint(0, 0), pixelSize);
- renderer->setDeviceRect(rect);
- renderer->setViewportRect(rect);
- if (QQuickRenderControl::renderWindowFor(q)) {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags);
- renderer->setDevicePixelRatio(devicePixelRatio);
- } else {
- const QSizeF logicalSize = pixelSize / redirect.devicePixelRatio;
- renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags);
- renderer->setDevicePixelRatio(redirect.devicePixelRatio);
- }
- } else {
- QSize pixelSize;
- QSizeF logicalSize;
- if (surfaceSize.isEmpty()) {
- pixelSize = size * devicePixelRatio;
- logicalSize = size;
- } else {
- pixelSize = surfaceSize;
- logicalSize = QSizeF(surfaceSize) / devicePixelRatio;
- }
- QRect rect(QPoint(0, 0), pixelSize);
- renderer->setDeviceRect(rect);
- renderer->setViewportRect(rect);
- renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
- renderer->setDevicePixelRatio(devicePixelRatio);
- }
+ QSize pixelSize;
+ if (redirect.rt.renderTarget)
+ pixelSize = redirect.rt.renderTarget->pixelSize();
+ else if (surfaceSize.isEmpty())
+ pixelSize = size * devicePixelRatio;
+ else
+ pixelSize = surfaceSize;
+ QSizeF logicalSize = QSizeF(pixelSize) / devicePixelRatio;
+
+ renderer->setDevicePixelRatio(devicePixelRatio);
+ renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize));
+ renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize));
+ renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags);
if (rhi) {
context->renderNextRhiFrame(renderer);
@@ -705,7 +684,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
QQuickWindowPrivate::QQuickWindowPrivate()
: contentItem(nullptr)
, dirtyItemList(nullptr)
- , devicePixelRatio(0)
+ , lastReportedItemDevicePixelRatio(0)
, context(nullptr)
, renderer(nullptr)
, windowManager(nullptr)
@@ -770,7 +749,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_ASSERT(windowManager || renderControl);
if (QScreen *screen = q->screen()) {
- devicePixelRatio = screen->devicePixelRatio();
+ lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio();
// if the screen changes, then QQuickWindow::handleScreenChanged disconnects
// and connects to the new screen
physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
@@ -1326,6 +1305,8 @@ QQuickItem *QQuickWindow::contentItem() const
\brief The item which currently has active focus or \c null if there is
no item with active focus.
+
+ \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
*/
QQuickItem *QQuickWindow::activeFocusItem() const
{
@@ -3736,21 +3717,31 @@ void QQuickWindow::runJobsAfterSwap()
}
/*!
- * Returns the device pixel ratio for this window.
- *
- * This is different from QWindow::devicePixelRatio() in that it supports
- * redirected rendering via QQuickRenderControl. When using a
- * QQuickRenderControl, the QQuickWindow is often not created, meaning it is
- * never shown and there is no underlying native window created in the
- * windowing system. As a result, querying properties like the device pixel
- * ratio cannot give correct results. Use this function instead.
- *
- * \sa QWindow::devicePixelRatio()
+ Returns the device pixel ratio for this window.
+
+ This is different from QWindow::devicePixelRatio() in that it supports
+ redirected rendering via QQuickRenderControl and QQuickRenderTarget. When
+ using a QQuickRenderControl, the QQuickWindow is often not fully created,
+ meaning it is never shown and there is no underlying native window created
+ in the windowing system. As a result, querying properties like the device
+ pixel ratio cannot give correct results. This function takes into account
+ both QQuickRenderControl::renderWindowFor() and
+ QQuickRenderTarget::devicePixelRatio(). When no redirection is in effect,
+ the result is same as QWindow::devicePixelRatio().
+
+ \sa QQuickRenderControl, QQuickRenderTarget, setRenderTarget(), QWindow::devicePixelRatio()
*/
qreal QQuickWindow::effectiveDevicePixelRatio() const
{
+ Q_D(const QQuickWindow);
QWindow *w = QQuickRenderControl::renderWindowFor(const_cast<QQuickWindow *>(this));
- return w ? w->devicePixelRatio() : devicePixelRatio();
+ if (w)
+ return w->devicePixelRatio();
+
+ if (!d->customRenderTarget.isNull())
+ return d->customRenderTarget.devicePixelRatio();
+
+ return devicePixelRatio();
}
/*!
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 0e69d63239..0fd09a1e33 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -193,7 +193,7 @@ public:
QVector<QQuickItem *> itemsToPolish;
- qreal devicePixelRatio;
+ qreal lastReportedItemDevicePixelRatio;
QMetaObject::Connection physicalDpiChangedConnection;
void updateDirtyNodes();
@@ -228,7 +228,6 @@ public:
struct Redirect {
QRhiCommandBuffer *commandBuffer = nullptr;
QQuickWindowRenderTarget rt;
- qreal devicePixelRatio = 1.0;
bool renderTargetDirty = false;
} redirect;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 95e6d35c1e..bda0085818 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -930,11 +930,10 @@ static void qsg_wipeBuffer(Buffer *buffer)
free(buffer->data);
}
-static void qsg_wipeBatch(Batch *batch, bool separateIndexBuffer)
+static void qsg_wipeBatch(Batch *batch)
{
qsg_wipeBuffer(&batch->vbo);
- if (separateIndexBuffer)
- qsg_wipeBuffer(&batch->ibo);
+ qsg_wipeBuffer(&batch->ibo);
delete batch->ubuf;
batch->stencilClipState.reset();
delete batch;
@@ -944,13 +943,12 @@ Renderer::~Renderer()
{
if (m_rhi) {
// Clean up batches and buffers
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
- qsg_wipeBatch(m_opaqueBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_opaqueBatches.at(i));
for (int i = 0; i < m_alphaBatches.size(); ++i)
- qsg_wipeBatch(m_alphaBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_alphaBatches.at(i));
for (int i = 0; i < m_batchPool.size(); ++i)
- qsg_wipeBatch(m_batchPool.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_batchPool.at(i));
}
for (Node *n : qAsConst(m_nodes))
@@ -1008,8 +1006,7 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
if (m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
- QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
- ? m_indexUploadPool : m_vertexUploadPool;
+ QDataBuffer<char> &pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
if (byteSize > pool.size())
pool.resize(byteSize);
buffer->data = pool.data();
@@ -2075,11 +2072,7 @@ void Renderer::uploadBatch(Batch *b)
ibufferSize = unmergedIndexSize;
}
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
+ map(&b->ibo, ibufferSize, true);
map(&b->vbo, bufferSize);
if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
@@ -2089,9 +2082,7 @@ void Renderer::uploadBatch(Batch *b)
if (b->merged) {
char *vertexData = b->vbo.data;
char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(useDepthBuffer()) * b->vertexCount * sizeof(float));
+ char *indexData = b->ibo.data;
quint16 iOffset16 = 0;
quint32 iOffset32 = 0;
@@ -2103,8 +2094,8 @@ void Renderer::uploadBatch(Batch *b)
const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
int indicesInSet = 0;
b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ int drawSetIndices = 0;
+ const char *indexBase = b->ibo.data;
b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
while (e) {
verticesInSet += e->node->geometry()->vertexCount();
@@ -2138,8 +2129,7 @@ void Renderer::uploadBatch(Batch *b)
}
} else {
char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
+ char *iboData = b->ibo.data;
Element *e = b->first;
while (e) {
QSGGeometry *g = e->node->geometry();
@@ -2201,9 +2191,7 @@ void Renderer::uploadBatch(Batch *b)
if (!b->drawSets.isEmpty()) {
if (m_uint32IndexForRhi) {
- const quint32 *id = (const quint32 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint32 *id = (const quint32 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2214,9 +2202,7 @@ void Renderer::uploadBatch(Batch *b)
}
}
} else {
- const quint16 *id = (const quint16 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint16 *id = (const quint16 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2237,8 +2223,7 @@ void Renderer::uploadBatch(Batch *b)
#endif // QT_NO_DEBUG_OUTPUT
unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->ibo, true);
if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
@@ -2669,6 +2654,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms,
ps->setFlags(flags);
ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
+ ps->setPolygonMode(m_gstate.polygonMode);
QRhiGraphicsPipeline::TargetBlend blend;
blend.colorWrite = m_gstate.colorWrite;
@@ -2808,13 +2794,14 @@ static void rendererToMaterialGraphicsState(QSGMaterialShader::GraphicsPipelineS
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
-
+ Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::Line) == int(QRhiGraphicsPipeline::Line));
dst->srcColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->srcColor);
dst->dstColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->dstColor);
dst->colorWrite = QSGMaterialShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
dst->cullMode = QSGMaterialShader::GraphicsPipelineState::CullMode(src->cullMode);
+ dst->polygonMode = QSGMaterialShader::GraphicsPipelineState::PolygonMode(src->polygonMode);
}
static void materialToRendererGraphicsState(GraphicsState *dst,
@@ -2825,6 +2812,7 @@ static void materialToRendererGraphicsState(GraphicsState *dst,
dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+ dst->polygonMode = QRhiGraphicsPipeline::PolygonMode(src->polygonMode);
}
void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
@@ -3695,7 +3683,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
if (largestVBO * 2 < m_vertexUploadPool.size())
m_vertexUploadPool.resize(largestVBO * 2);
- if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size())
+ if (largestIBO * 2 < m_indexUploadPool.size())
m_indexUploadPool.resize(largestIBO * 2);
if (Q_UNLIKELY(debug_render())) {
@@ -3727,6 +3715,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
m_gstate.blending = false;
m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.polygonMode = QRhiGraphicsPipeline::Fill;
m_gstate.colorWrite = QRhiGraphicsPipeline::R
| QRhiGraphicsPipeline::G
| QRhiGraphicsPipeline::B
@@ -4042,7 +4031,8 @@ bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
&& a.stencilTest == b.stencilTest
&& a.sampleCount == b.sampleCount
&& a.drawMode == b.drawMode
- && a.lineWidth == b.lineWidth;
+ && a.lineWidth == b.lineWidth
+ && a.polygonMode == b.polygonMode;
}
bool operator!=(const GraphicsState &a, const GraphicsState &b) noexcept
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 241f5748b6..5cfc6c54c6 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -640,6 +640,7 @@ struct GraphicsState
int sampleCount = 1;
QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
float lineWidth = 1.0f;
+ QRhiGraphicsPipeline::PolygonMode polygonMode = QRhiGraphicsPipeline::Fill;
};
bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
index bd17fd365a..3eb544f94d 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -684,6 +684,23 @@ bool QSGMaterialShader::updateGraphicsPipelineState(RenderState &state, Graphics
*/
/*!
+ \enum QSGMaterialShader::GraphicsPipelineState::PolygonMode
+ \since 6.4
+ \brief Specifies the polygon rasterization mode
+
+ Polygon Mode (Triangle Fill Mode in Metal, Fill Mode in D3D) specifies
+ the fill mode used when rasterizing polygons. Polygons may be drawn as
+ solids (Fill), or as a wire mesh (Line).
+
+ \warning OpenGL ES does not support the \c{Line} polygon mode. OpenGL ES
+ will rasterize all polygons as filled no matter what polygon mode is set.
+ Using \c{Line} will make your application non-portable.
+
+ \value Fill The interior of the polygon is filled (default)
+ \value Line Boundary edges of the polygon are drawn as line segments.
+ */
+
+/*!
Returns the accumulated opacity to be used for rendering.
*/
float QSGMaterialShader::RenderState::opacity() const
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
index 566f954b06..a3c8afb875 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -130,12 +130,18 @@ public:
CullBack
};
+ enum PolygonMode {
+ Fill,
+ Line,
+ };
+
bool blendEnable;
BlendFactor srcColor;
BlendFactor dstColor;
ColorMask colorWrite;
QColor blendConstant;
CullMode cullMode;
+ PolygonMode polygonMode;
// This struct is extensible while keeping BC since apps only ever get
// a ptr to the struct, it is not created by them.
};
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 19b8cf9354..49dc056c89 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -40,6 +40,7 @@
#ifndef QSGNODE_H
#define QSGNODE_H
+#include <QtCore/qlist.h>
#include <QtQuick/qsggeometry.h>
#include <QtGui/QMatrix4x4>
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index aa9f78f2c5..7d71c5248c 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -53,6 +53,7 @@
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgtexture.h>
+#include <QtQuick/qquickpainteditem.h>
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
#include <QtGui/qbrush.h>
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 8579cb5e2a..a4337a58ca 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -62,7 +62,6 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
, m_rhiAtlasManager(nullptr)
, m_currentFrameCommandBuffer(nullptr)
, m_currentFrameRenderPass(nullptr)
- , m_separateIndexBuffer(false)
, m_useDepthBufferFor2D(true)
, m_glyphCacheResourceUpdates(nullptr)
{
@@ -87,8 +86,6 @@ void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *par
m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
if (!m_rhiAtlasManager)
m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
- // unlike OpenGL (and like WebGL), QRhi does not guarantee buffer usage types can be mixed
- m_separateIndexBuffer = true;
m_glyphCacheResourceUpdates = nullptr;
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index e96bf045b5..a97347eaf0 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -120,7 +120,6 @@ public:
virtual void initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant);
int maxTextureSize() const override { return m_maxTextureSize; }
- bool separateIndexBuffer() const { return m_separateIndexBuffer; }
bool useDepthBufferFor2D() const { return m_useDepthBufferFor2D; }
int msaaSampleCount() const { return m_initParams.sampleCount; }
@@ -159,7 +158,6 @@ protected:
QRhiCommandBuffer *m_currentFrameCommandBuffer;
QRhiRenderPassDescriptor *m_currentFrameRenderPass;
qreal m_currentDevicePixelRatio;
- bool m_separateIndexBuffer;
bool m_useDepthBufferFor2D;
QRhiResourceUpdateBatch *m_glyphCacheResourceUpdates;
};
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 98823794c4..b96f75ddbd 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -105,10 +105,6 @@ void QSGRenderLoop::cleanup()
}
delete s_instance;
s_instance = nullptr;
-
-#ifdef ENABLE_DEFAULT_BACKEND
- QSGRhiSupport::cleanupDefaultVulkanInstance();
-#endif
}
QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 7a2aeab7a6..da83e82b4b 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -39,14 +39,15 @@
#include "qsgrhisupport_p.h"
#include "qsgcontext_p.h"
-# include "qsgdefaultrendercontext_p.h"
+#include "qsgdefaultrendercontext_p.h"
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtGui/qwindow.h>
+
#if QT_CONFIG(vulkan)
-#include <QtGui/qvulkaninstance.h>
+#include <QtGui/private/qvulkandefaultinstance_p.h>
#endif
#include <QOperatingSystemVersion>
@@ -54,69 +55,6 @@
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(vulkan)
-QVulkanInstance *s_vulkanInstance = nullptr;
-#endif
-
-QVulkanInstance *QSGRhiSupport::defaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- QSGRhiSupport *inst = QSGRhiSupport::instance();
- if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan)
- return nullptr;
-
- if (!s_vulkanInstance) {
- s_vulkanInstance = new QVulkanInstance;
-
- // With a Vulkan implementation >= 1.1 we can check what
- // vkEnumerateInstanceVersion() says and request 1.2 or 1.1 based on the
- // result. To prevent future surprises, be conservative and ignore any > 1.2
- // versions for now. For 1.0 implementations nothing will be requested, the
- // default 0 in VkApplicationInfo means 1.0.
- //
- // Vulkan 1.0 is actually sufficient for 99% of Qt Quick (3D)'s
- // functionality. In addition, Vulkan implementations tend to enable 1.1 and 1.2
- // functionality regardless of the VkInstance API request. However, the
- // validation layer seems to take this fairly seriously, so we should be
- // prepared for using 1.1 and 1.2 features in a fully correct manner. This also
- // helps custom Vulkan code in applications, which is not under out control; it
- // is ideal if Vulkan 1.1 and 1.2 are usable without requiring such applications
- // to create their own QVulkanInstance just to be able to make an appropriate
- // setApiVersion() call on it.
-
- const QVersionNumber supportedVersion = s_vulkanInstance->supportedApiVersion();
- if (supportedVersion >= QVersionNumber(1, 2))
- s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
- else if (supportedVersion >= QVersionNumber(1, 1))
- s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
- qCDebug(QSG_LOG_INFO) << "Requesting Vulkan API" << s_vulkanInstance->apiVersion()
- << "Instance-level version was reported as" << supportedVersion;
-
- if (inst->isDebugLayerRequested())
- s_vulkanInstance->setLayers({ "VK_LAYER_KHRONOS_validation" });
-
- s_vulkanInstance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
-
- if (!s_vulkanInstance->create()) {
- qWarning("Failed to create Vulkan instance");
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
- }
- }
- return s_vulkanInstance;
-#else
- return nullptr;
-#endif
-}
-
-void QSGRhiSupport::cleanupDefaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
-#endif
-}
-
QSGRhiSupport::QSGRhiSupport()
: m_settingsApplied(false),
m_enableRhi(false),
@@ -256,21 +194,14 @@ void QSGRhiSupport::applySettings()
void QSGRhiSupport::adjustToPlatformQuirks()
{
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-
- // ### For now just create a throwaway QRhi instance. This will be replaced
- // by a more lightweight way, once a helper function is added gui/rhi.
-
// A macOS VM may not have Metal support at all. We have to decide at this
// point, it will be too late afterwards, and the only way is to see if
// MTLCreateSystemDefaultDevice succeeds.
if (m_rhiBackend == QRhi::Metal) {
QRhiMetalInitParams rhiParams;
- QRhi *tempRhi = QRhi::create(m_rhiBackend, &rhiParams, {});
- if (!tempRhi) {
+ if (!QRhi::probe(m_rhiBackend, &rhiParams)) {
m_rhiBackend = QRhi::OpenGLES2;
qCDebug(QSG_LOG_INFO, "Metal does not seem to be supported. Falling back to OpenGL.");
- } else {
- delete tempRhi;
}
}
#endif
@@ -574,8 +505,14 @@ void QSGRhiSupport::prepareWindowForRhi(QQuickWindow *window)
// always be under the application's control then (since the default
// instance we could create here would not be configurable by the
// application in any way, and that is often not acceptable).
- if (!window->vulkanInstance() && !wd->renderControl)
- window->setVulkanInstance(QSGRhiSupport::defaultVulkanInstance());
+ if (!window->vulkanInstance() && !wd->renderControl) {
+ QVulkanInstance *vkinst = QVulkanDefaultInstance::instance();
+ if (vkinst)
+ qCDebug(QSG_LOG_INFO) << "Got Vulkan instance from QVulkanDefaultInstance, requested api version was" << vkinst->apiVersion();
+ else
+ qCDebug(QSG_LOG_INFO) << "No Vulkan instance from QVulkanDefaultInstance, expect problems";
+ window->setVulkanInstance(vkinst);
+ }
}
#else
Q_UNUSED(window);
@@ -648,6 +585,8 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QO
#endif
#if QT_CONFIG(vulkan)
if (backend == QRhi::Vulkan) {
+ if (isDebugLayerRequested())
+ QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation, true);
QRhiVulkanInitParams rhiParams;
prepareWindowForRhi(window); // sets a vulkanInstance if not yet present
rhiParams.inst = window->vulkanInstance();
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
index 90cc8328f9..101941875e 100644
--- a/src/quick/scenegraph/qsgrhisupport_p.h
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -77,7 +77,6 @@
QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class QVulkanInstance;
class QOffscreenSurface;
// Opting in/out of QRhi and choosing the default/requested backend is managed
@@ -85,9 +84,6 @@ class QOffscreenSurface;
// creating a render loop. A well-written render loop sets up its QRhi and
// related machinery using the helper functions in here.
//
-// cleanup() must be called to perform global (not per thread) cleanup, such
-// as, destroying the QVulkanInstance (if one was created in vulkanInstance()).
-//
// In addition, the class provides handy conversion and query stuff for the
// renderloop and the QSGRendererInterface implementations.
//
@@ -96,8 +92,6 @@ class Q_QUICK_PRIVATE_EXPORT QSGRhiSupport
public:
static QSGRhiSupport *instance_internal();
static QSGRhiSupport *instance();
- static QVulkanInstance *defaultVulkanInstance();
- static void cleanupDefaultVulkanInstance();
static int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi);
static QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiCommandBuffer *cb, QRhiTexture *src = nullptr);
static void checkEnvQSgInfo();
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
index 2a7be48d3d..5541c4f2da 100644
--- a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
@@ -67,8 +67,13 @@ Manager::Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSu
, m_rhi(rc->rhi())
{
const int maxSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
- int w = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfacePixelSize.width() - 1))));
- int h = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfacePixelSize.height() - 1))));
+ // surfacePixelSize is just a hint that was passed in when initializing the
+ // rendercontext, likely based on the window size, if it was available,
+ // that is. Therefore, it may be anything, incl. zero and negative.
+ const int widthHint = qMax(1, surfacePixelSize.width());
+ const int heightHint = qMax(1, surfacePixelSize.height());
+ int w = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(widthHint - 1))));
+ int h = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(heightHint - 1))));
if (maybeSurface && maybeSurface->surfaceClass() == QSurface::Window) {
QWindow *window = static_cast<QWindow *>(maybeSurface);
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 82cdd03acc..92f66af011 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -309,6 +309,8 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o);
+ Q_ASSERT(m_texture);
+ Q_ASSERT(other->texture());
const qint64 diff = m_texture->comparisonKey() - other->texture()->comparisonKey();
if (diff != 0)
return diff < 0 ? -1 : 1;
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index 0ed3990738..e850249b65 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -210,6 +210,32 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \qmlproperty StyleHints Application::styleHints
+
+ The \c styleHints property provides platform-specific style hints and settings.
+ See the \l QStyleHints documentation for further details.
+
+ The following example uses \c styleHints to determine whether an
+ item should gain focus on mouse press or touch release:
+ \code
+ import QtQuick
+
+ MouseArea {
+ id: button
+
+ onPressed: {
+ if (!Application.styleHints.setFocusOnTouchRelease)
+ button.forceActiveFocus()
+ }
+ onReleased: {
+ if (Application.styleHints.setFocusOnTouchRelease)
+ button.forceActiveFocus()
+ }
+ }
+ \endcode
+ */
+
+/*!
\qmlsignal Application::aboutToQuit()
This signal is emitted when the application is about to quit the main
@@ -273,6 +299,11 @@ QString QQuickApplication::displayName() const
return QGuiApplication::applicationDisplayName();
}
+QStyleHints *QQuickApplication::styleHints()
+{
+ return QGuiApplication::styleHints();
+}
+
void QQuickApplication::setDisplayName(const QString &displayName)
{
return QGuiApplication::setApplicationDisplayName(displayName);
diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h
index dd67b4b11d..d8f4b3a6ee 100644
--- a/src/quick/util/qquickapplication_p.h
+++ b/src/quick/util/qquickapplication_p.h
@@ -58,6 +58,7 @@
#include <QtQml/private/qqmlglobal_p.h>
#include <QtGui/qfont.h>
+#include <QtGui/qstylehints.h>
#include <QtCore/qobject.h>
@@ -73,6 +74,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickApplication : public QQmlApplication
Q_PROPERTY(QFont font READ font CONSTANT)
Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QQmlListProperty<QQuickScreenInfo> screens READ screens NOTIFY screensChanged)
+ Q_PROPERTY(QStyleHints *styleHints READ styleHints CONSTANT)
QML_NAMED_ELEMENT(Application)
QML_SINGLETON
@@ -89,6 +91,7 @@ public:
QQmlListProperty<QQuickScreenInfo> screens();
QString displayName() const;
void setDisplayName(const QString &displayName);
+ QStyleHints *styleHints();
Q_SIGNALS:
void activeChanged();
diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp
index 3242defceb..a5c40f6139 100644
--- a/src/quick/util/qquickfontloader.cpp
+++ b/src/quick/util/qquickfontloader.cpp
@@ -58,6 +58,7 @@
#endif
#include <QtCore/QCoreApplication>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <QtGui/private/qfontdatabase_p.h>
@@ -172,13 +173,10 @@ public:
void reset()
{
- QVector<QQuickFontObject *> deleted;
- QHash<QUrl, QQuickFontObject*>::iterator it;
- for (it = map.begin(); it != map.end(); ++it) {
- if (!deleted.contains(it.value())) {
- deleted.append(it.value());
- delete it.value();
- }
+ QDuplicateTracker<QQuickFontObject *, 256> deleted(map.size());
+ for (QQuickFontObject *fo : std::as_const(map)) {
+ if (!deleted.hasSeen(fo))
+ delete fo;
}
map.clear();
}
diff --git a/src/quick/util/qquickforeignutils_p.h b/src/quick/util/qquickforeignutils_p.h
index 4d6288bc36..0f7869531f 100644
--- a/src/quick/util/qquickforeignutils_p.h
+++ b/src/quick/util/qquickforeignutils_p.h
@@ -67,6 +67,14 @@
QT_BEGIN_NAMESPACE
+struct QStyleHintsForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QStyleHints)
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 4)
+};
+
#if QT_CONFIG(validator)
struct QValidatorForeign
{
@@ -95,6 +103,7 @@ struct QInputMethodForeign
QML_FOREIGN(QInputMethod)
QML_NAMED_ELEMENT(InputMethod)
QML_ADDED_IN_VERSION(2, 0)
+ QML_REMOVED_IN_VERSION(6, 4)
QML_UNCREATABLE("InputMethod is an abstract class.")
};
#endif // QT_CONFIG(im)
diff --git a/src/quick/util/qquickinputmethod.cpp b/src/quick/util/qquickinputmethod.cpp
new file mode 100644
index 0000000000..09f1e63016
--- /dev/null
+++ b/src/quick/util/qquickinputmethod.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickinputmethod_p.h"
+
+#include <QtGui/qguiapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype InputMethod
+ \inqmlmodule QtQuick.
+
+ \brief Provides access to \l QInputMethod for QML applications.
+
+ The InputMethod singleton allows access to application's \l QInputMethod object
+ and all its properties and slots. See the \l QInputMethod documentation for
+ further details.
+*/
+
+QQuickInputMethod::QQuickInputMethod(QObject *parent) : QObject(parent)
+{
+ QInputMethod *inputMethod = QGuiApplication::inputMethod();
+ connect(inputMethod, &QInputMethod::anchorRectangleChanged, this,
+ &QQuickInputMethod::anchorRectangleChanged);
+ connect(inputMethod, &QInputMethod::animatingChanged, this,
+ &QQuickInputMethod::animatingChanged);
+ connect(inputMethod, &QInputMethod::cursorRectangleChanged, this,
+ &QQuickInputMethod::cursorRectangleChanged);
+ connect(inputMethod, &QInputMethod::inputDirectionChanged, this,
+ &QQuickInputMethod::inputDirectionChanged);
+ connect(inputMethod, &QInputMethod::inputItemClipRectangleChanged, this,
+ &QQuickInputMethod::inputItemClipRectangleChanged);
+ connect(inputMethod, &QInputMethod::keyboardRectangleChanged, this,
+ &QQuickInputMethod::keyboardRectangleChanged);
+ connect(inputMethod, &QInputMethod::localeChanged, this, &QQuickInputMethod::localeChanged);
+ connect(inputMethod, &QInputMethod::visibleChanged, this, &QQuickInputMethod::visibleChanged);
+}
+
+void QQuickInputMethod::commit()
+{
+ QGuiApplication::inputMethod()->commit();
+}
+void QQuickInputMethod::hide()
+{
+ QGuiApplication::inputMethod()->hide();
+}
+void QQuickInputMethod::invokeAction(QInputMethod::Action a, int cursorPosition)
+{
+ QGuiApplication::inputMethod()->invokeAction(a, cursorPosition);
+}
+void QQuickInputMethod::reset()
+{
+ QGuiApplication::inputMethod()->reset();
+}
+void QQuickInputMethod::show()
+{
+ QGuiApplication::inputMethod()->show();
+}
+void QQuickInputMethod::update(Qt::InputMethodQueries queries)
+{
+ QGuiApplication::inputMethod()->update(queries);
+}
+
+QRectF QQuickInputMethod::anchorRectangle() const
+{
+ return QGuiApplication::inputMethod()->cursorRectangle();
+}
+QRectF QQuickInputMethod::cursorRectangle() const
+{
+ return QGuiApplication::inputMethod()->cursorRectangle();
+}
+Qt::LayoutDirection QQuickInputMethod::inputDirection() const
+{
+ return QGuiApplication::inputMethod()->inputDirection();
+}
+QRectF QQuickInputMethod::inputItemClipRectangle() const
+{
+ return QGuiApplication::inputMethod()->inputItemClipRectangle();
+}
+
+QRectF QQuickInputMethod::inputItemRectangle() const
+{
+ return QGuiApplication::inputMethod()->inputItemRectangle();
+}
+void QQuickInputMethod::setInputItemRectangle(const QRectF &rect)
+{
+ QGuiApplication::inputMethod()->setInputItemRectangle(rect);
+}
+
+QTransform QQuickInputMethod::inputItemTransform() const
+{
+ return QGuiApplication::inputMethod()->inputItemTransform();
+}
+void QQuickInputMethod::setInputItemTransform(const QTransform &transform)
+{
+ QGuiApplication::inputMethod()->setInputItemTransform(transform);
+}
+
+bool QQuickInputMethod::isAnimating() const
+{
+ return QGuiApplication::inputMethod()->isAnimating();
+}
+
+bool QQuickInputMethod::isVisible() const
+{
+ return QGuiApplication::inputMethod()->isVisible();
+}
+void QQuickInputMethod::setVisible(bool visible)
+{
+ QGuiApplication::inputMethod()->setVisible(visible);
+}
+
+QRectF QQuickInputMethod::keyboardRectangle() const
+{
+ return QGuiApplication::inputMethod()->keyboardRectangle();
+}
+QLocale QQuickInputMethod::locale() const
+{
+ return QGuiApplication::inputMethod()->locale();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/util/qquickinputmethod_p.h b/src/quick/util/qquickinputmethod_p.h
new file mode 100644
index 0000000000..fe94da667b
--- /dev/null
+++ b/src/quick/util/qquickinputmethod_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKINPUTMETHOD_P_H
+#define QQUICKINPUTMETHOD_P_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/qobject.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qinputmethod.h>
+#include <QtQml/qqml.h>
+
+#include <private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+class Q_QUICK_PRIVATE_EXPORT QQuickInputMethod : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(InputMethod)
+ QML_ADDED_IN_VERSION(6, 4)
+ QML_SINGLETON
+
+ Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
+ Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged)
+ Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle NOTIFY keyboardRectangleChanged)
+ Q_PROPERTY(QRectF inputItemClipRectangle READ inputItemClipRectangle NOTIFY
+ inputItemClipRectangleChanged)
+ Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
+ Q_PROPERTY(bool animating READ isAnimating NOTIFY animatingChanged)
+ Q_PROPERTY(QLocale locale READ locale NOTIFY localeChanged)
+ Q_PROPERTY(Qt::LayoutDirection inputDirection READ inputDirection NOTIFY inputDirectionChanged)
+public:
+ explicit QQuickInputMethod(QObject *parent = nullptr);
+
+ QRectF anchorRectangle() const;
+ QRectF cursorRectangle() const;
+ Qt::LayoutDirection inputDirection() const;
+ QRectF inputItemClipRectangle() const;
+
+ QRectF inputItemRectangle() const;
+ void setInputItemRectangle(const QRectF &rect);
+
+ QTransform inputItemTransform() const;
+ void setInputItemTransform(const QTransform &transform);
+
+ bool isAnimating() const;
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
+ QRectF keyboardRectangle() const;
+ QLocale locale() const;
+signals:
+ void anchorRectangleChanged();
+ void animatingChanged();
+ void cursorRectangleChanged();
+ void inputDirectionChanged(Qt::LayoutDirection newDirection);
+ void inputItemClipRectangleChanged();
+ void keyboardRectangleChanged();
+ void localeChanged();
+ void visibleChanged();
+
+public slots:
+ void commit();
+ void hide();
+ void invokeAction(QInputMethod::Action a, int cursorPosition);
+ void reset();
+ void show();
+ void update(Qt::InputMethodQueries queries);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKINPUTMETHOD_P_H
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index 5a4c72848c..f8af708ae7 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -62,6 +62,7 @@ QT_REQUIRE_CONFIG(quick_path);
#include <private/qtquickglobal_p.h>
#include <QtCore/QObject>
+#include <QtCore/QHash>
#include <QtGui/QPainterPath>
#include <QtGui/QFont>
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index f403615d83..9ecdbc1846 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -328,6 +328,7 @@ public:
QQuickImageProviderOptions::AutoTransform appliedTransform;
QColorSpace targetColorSpace;
+ QIODevice *specialDevice = nullptr;
QQuickTextureFactory *textureFactory;
QIntrusiveList<QQuickPixmap, &QQuickPixmap::dataListNode> declarativePixmaps;
@@ -895,40 +896,54 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
QImage image;
QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
QString errorStr;
- QFile f(existingImageFileForPath(localFile));
QSize readSize;
- if (f.open(QIODevice::ReadOnly)) {
- QSGTextureReader texReader(&f, localFile);
- if (backendSupport()->hasOpenGL && texReader.isTexture()) {
- QQuickTextureFactory *factory = texReader.read();
- if (factory) {
- readSize = factory->textureSize();
+
+ if (runningJob->data && runningJob->data->specialDevice) {
+ int frameCount;
+ int const frame = runningJob->data ? runningJob->data->frame : 0;
+ if (!readImage(url, runningJob->data->specialDevice, &image, &errorStr, &readSize, &frameCount,
+ runningJob->requestRegion, runningJob->requestSize,
+ runningJob->providerOptions, nullptr, frame)) {
+ errorCode = QQuickPixmapReply::Loading;
+ } else if (runningJob->data) {
+ runningJob->data->frameCount = frameCount;
+ }
+ } else {
+ QFile f(existingImageFileForPath(localFile));
+ if (f.open(QIODevice::ReadOnly)) {
+ QSGTextureReader texReader(&f, localFile);
+ if (backendSupport()->hasOpenGL && texReader.isTexture()) {
+ QQuickTextureFactory *factory = texReader.read();
+ if (factory) {
+ readSize = factory->textureSize();
+ } else {
+ errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
+ if (f.fileName() != localFile)
+ errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
+ errorCode = QQuickPixmapReply::Decoding;
+ }
+ mutex.lock();
+ if (!cancelled.contains(runningJob))
+ runningJob->postReply(errorCode, errorStr, readSize, factory);
+ mutex.unlock();
+ return;
} else {
- errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
- if (f.fileName() != localFile)
- errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
- errorCode = QQuickPixmapReply::Decoding;
+ int frameCount;
+ int const frame = runningJob->data ? runningJob->data->frame : 0;
+ if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
+ runningJob->requestRegion, runningJob->requestSize,
+ runningJob->providerOptions, nullptr, frame)) {
+ errorCode = QQuickPixmapReply::Loading;
+ if (f.fileName() != localFile)
+ errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
+ } else if (runningJob->data) {
+ runningJob->data->frameCount = frameCount;
+ }
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
- runningJob->postReply(errorCode, errorStr, readSize, factory);
- mutex.unlock();
- return;
} else {
- int frameCount;
- int const frame = runningJob->data ? runningJob->data->frame : 0;
- if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount, runningJob->requestRegion, runningJob->requestSize,
- runningJob->providerOptions, nullptr, frame)) {
- errorCode = QQuickPixmapReply::Loading;
- if (f.fileName() != localFile)
- errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
- } else if (runningJob->data) {
- runningJob->data->frameCount = frameCount;
- }
+ errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
+ errorCode = QQuickPixmapReply::Loading;
}
- } else {
- errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
- errorCode = QQuickPixmapReply::Loading;
}
mutex.lock();
if (!cancelled.contains(runningJob))
@@ -1738,6 +1753,47 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
}
}
+/*! \internal
+ Attempts to load an image from the given \a url via the given \a device.
+ This is for special cases when the QImageIOHandler can benefit from reusing
+ the I/O device, or from something extra that a subclass of QIODevice
+ carries with it. So far, this code doesn't support loading anything other
+ than a QImage, for example compressed textures. It can be added if needed.
+*/
+void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
+ const QRect &requestRegion, const QSize &requestSize,
+ const QQuickImageProviderOptions &providerOptions, int frame, int frameCount)
+{
+ auto oldD = d;
+ QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
+ QQuickPixmapStore *store = pixmapStore();
+ QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
+ iter = store->m_cache.find(key);
+ if (iter == store->m_cache.end()) {
+ if (!engine)
+ return;
+
+ d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions,
+ QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
+ d->specialDevice = device;
+ d->addToCache();
+
+ QQuickPixmapReader::readerMutex.lock();
+ d->reply = QQuickPixmapReader::instance(engine)->getImage(d);
+ if (oldD) {
+ QObject::connect(d->reply, &QQuickPixmapReply::finished, [oldD, this]() {
+ oldD->declarativePixmaps.remove(this);
+ oldD->release();
+ });
+ }
+ QQuickPixmapReader::readerMutex.unlock();
+ } else {
+ d = *iter;
+ d->addref();
+ d->declarativePixmaps.insert(this);
+ }
+}
+
void QQuickPixmap::clear()
{
if (d) {
diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h
index 5600443d31..68691b54e3 100644
--- a/src/quick/util/qquickpixmapcache_p.h
+++ b/src/quick/util/qquickpixmapcache_p.h
@@ -174,6 +174,9 @@ public:
void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize,
QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1,
qreal devicePixelRatio = 1.0);
+ void loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
+ const QRect &requestRegion, const QSize &requestSize,
+ const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
void clear();
void clear(QObject *);
diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h
index d75cfc32ee..3f0545df91 100644
--- a/src/quick/util/qquickstate_p_p.h
+++ b/src/quick/util/qquickstate_p_p.h
@@ -212,13 +212,19 @@ public:
struct OperationGuard : public QQmlGuard<QQuickStateOperation>
{
- OperationGuard(QObject *obj, QList<OperationGuard> *l) : list(l) {
+ OperationGuard(QObject *obj, QList<OperationGuard> *l) : QQmlGuard<QQuickStateOperation>(
+ OperationGuard::objectDestroyedImpl, nullptr)
+ ,list(l)
+ {
setObject(static_cast<QQuickStateOperation *>(obj));
}
QList<OperationGuard> *list;
- void objectDestroyed(QQuickStateOperation *) override {
+
+ private:
+ static void objectDestroyedImpl(QQmlGuardImpl *guard) {
+ auto This = static_cast<OperationGuard *>(guard);
// we assume priv will always be destroyed after objectDestroyed calls
- list->removeOne(*this);
+ This->list->removeOne(*This);
}
};
QList<OperationGuard> operations;
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index d664b67485..781d2cf1fb 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -376,7 +376,15 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when()) {
+ bool whenValue = state->when();
+ const QQmlProperty whenProp(state, u"when"_qs);
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually reevaluate the binding
+ if (auto abstractBinding = dynamic_cast<QQmlBinding *>( potentialWhenBinding.asAbstractBinding()))
+ whenValue = abstractBinding->evaluate().toBool();
+ if (whenValue) {
qCDebug(lcStates) << "Setting auto state due to expression";
if (currentState != state->name()) {
q->setState(state->name());
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index c309460263..7e93d87555 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
interpret the number and will accept locale specific digits, group separators, and positive
and negative signs. In addition, IntValidator is always guaranteed to accept a number
formatted according to the "C" locale.
+
+ \sa DoubleValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickIntValidator::QQuickIntValidator(QObject *parent)
@@ -131,6 +133,8 @@ void QQuickIntValidator::resetLocaleName()
it is also rejected. If \l notation is DoubleValidator.ScientificNotation,
and the input is not in the valid range, it is accecpted but invalid. The
value may yet become valid by changing the exponent.
+
+ \sa IntValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent)
@@ -210,6 +214,8 @@ void QQuickDoubleValidator::resetLocaleName()
The RegularExpressionValidator type provides a validator, that counts as valid any string which
matches a specified regular expression.
+
+ \sa IntValidator, DoubleValidator, {Validating Input Text}
*/
/*!
\qmlproperty regularExpression QtQuick::RegularExpressionValidator::regularExpression
diff --git a/src/quickcontrols2/CMakeLists.txt b/src/quickcontrols2/CMakeLists.txt
index 4848fc1b79..33c9bfaee7 100644
--- a/src/quickcontrols2/CMakeLists.txt
+++ b/src/quickcontrols2/CMakeLists.txt
@@ -12,7 +12,6 @@ qt_internal_add_qml_module(QuickControls2
IMPORTS
QtQuick.Controls.impl/auto
OPTIONAL_IMPORTS
- QtQuick.Controls.Basic/auto
QtQuick.Controls.Fusion/auto
QtQuick.Controls.Material/auto
QtQuick.Controls.Imagine/auto
@@ -20,6 +19,8 @@ qt_internal_add_qml_module(QuickControls2
QtQuick.Controls.Windows/auto
QtQuick.Controls.macOS/auto
QtQuick.Controls.iOS/auto
+ DEFAULT_IMPORTS
+ QtQuick.Controls.Basic/auto
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
SOURCES
diff --git a/src/quickcontrols2/basic/DayOfWeekRow.qml b/src/quickcontrols2/basic/DayOfWeekRow.qml
index 203fa5900c..9e555699df 100644
--- a/src/quickcontrols2/basic/DayOfWeekRow.qml
+++ b/src/quickcontrols2/basic/DayOfWeekRow.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/basic/MonthGrid.qml b/src/quickcontrols2/basic/MonthGrid.qml
index 797a52b8a0..a77dcd655a 100644
--- a/src/quickcontrols2/basic/MonthGrid.qml
+++ b/src/quickcontrols2/basic/MonthGrid.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/basic/WeekNumberColumn.qml b/src/quickcontrols2/basic/WeekNumberColumn.qml
index fb0d0b4591..f8e3900685 100644
--- a/src/quickcontrols2/basic/WeekNumberColumn.qml
+++ b/src/quickcontrols2/basic/WeekNumberColumn.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
diff --git a/src/quickcontrols2/doc/src/qt6-changes.qdoc b/src/quickcontrols2/doc/src/qt6-changes.qdoc
index 3c3fcdb022..61b80b3839 100644
--- a/src/quickcontrols2/doc/src/qt6-changes.qdoc
+++ b/src/quickcontrols2/doc/src/qt6-changes.qdoc
@@ -253,36 +253,36 @@
\section1 Controls
- \section2 ApplicationWindow
+ \section2 Changes to ApplicationWindow
The deprecated overlay properties and attached API were removed. Use the
\l Overlay attached type instead.
- \section2 ComboBox
+ \section2 Changes to ComboBox
The \l {ComboBox::}{pressed} property is now read-only. To modify the
visual pressed state of a ComboBox, use the \l {ComboBox::}{down} property
instead.
- \section2 Container
+ \section2 Changes to Container
The deprecated \c removeItem(var) function was removed.
\l {Container::}{removeItem(Item)} or \l {Container::}{takeItem(int)} can
be used instead.
- \section2 Dialog
+ \section2 Changes to Dialog
\l {Dialog}'s \l {Dialog::}{accepted()} and \l {Dialog::}{rejected()}
signals are now emitted before \l {Popup::}{closed()} when calling
\l {Dialog::}{done()}, \l {Dialog::}{accept()} and \l {Dialog::}{reject()}.
- \section2 Menu
+ \section2 Changes to Menu
The deprecated \c removeItem(var) function was removed.
\l {Menu::}{removeItem(Item)} or \l {Menu::}{takeItem(int)} can be used
instead.
- \section2 ToolTip
+ \section2 Changes to ToolTip
\l {ToolTip}'s timeout now begins only after \l {Popup::}{opened()} has
been emitted. This results in tooltips with enter transitions being visible
@@ -291,13 +291,13 @@
visually check tooltips in your application and adjust timeouts if
necessary.
- \section2 StackView
+ \section2 Changes to StackView
The StackView.Transition enum value was deprecated. The operation argument
can be omitted in order to use the default transition for any given
operation.
- \section2 Tumbler
+ \section2 Changes to Tumbler
\l {Item::}{implicitWidth} and \l {Item::}{implicitHeight} must now be
provided for \l {Tumbler}'s \l {Control::}{contentItem}, making it
diff --git a/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc b/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
index 6fa4447c83..3222eabf10 100644
--- a/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
+++ b/src/quickcontrols2/doc/src/qtquickcontrols2-customize.qdoc
@@ -321,6 +321,14 @@
it's important not to import \c {QtQuick.Controls}. Doing so will
prevent the QML from being compiled by the QML compiler.
+ \section4 Implement types used by other types
+
+ Suppose you were using ScrollViews in your application, and decided that
+ you want to customize their scroll bars. It is tempting to just implement a
+ custom ScrollBar.qml and have ScrollView pick up the customized ScrollBar
+ automatically. However, this will not work. You must implement both
+ ScrollBar.qml \e and ScrollView.qml.
+
\section3 Attached properties
It is common for a style to have certain properties or attributes that
diff --git a/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc b/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
index 7b64e776dd..889947019c 100644
--- a/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
+++ b/src/quickcontrols2/doc/src/qtquickcontrols2-index.qdoc
@@ -38,7 +38,7 @@
\image qtquickcontrols2-styles.png
- Qt Quick Controls comes with a selection customizable styles.
+ Qt Quick Controls comes with a selection of customizable styles.
See \l {Styling Qt Quick Controls} for more details.
@@ -55,9 +55,10 @@
\section2 C++ API
- Using the \l{Qt Quick Controls C++ Classes}{C++ API} requires linking against the module library,
- either directly or through other dependencies.
- Several build tools have dedicated support for this, including CMake and qmake.
+ Using the \l{Qt Quick Controls C++ Classes}{C++ API} requires linking
+ against the module library, either directly or through other dependencies.
+ Several build tools have dedicated support for this, including CMake and
+ qmake.
\section3 Building with CMake
Use the \c find_package() command to locate the needed module components in the Qt6 package:
@@ -144,6 +145,11 @@
\endtable
\section1 Module Evolution
+
+ Qt Quick Controls was originally written with touch interfaces as the primary focus.
+ While it is already possible to develop desktop interfaces, work is ongoing to
+ provide a more native look and feel.
+
\l{Changes to Qt Quick Controls} lists important changes in the
module API and functionality that were done for the Qt 6 series of Qt.
diff --git a/src/quickcontrols2/material/qquickmaterialstyle_p.h b/src/quickcontrols2/material/qquickmaterialstyle_p.h
index 9f353cff33..b05df26be6 100644
--- a/src/quickcontrols2/material/qquickmaterialstyle_p.h
+++ b/src/quickcontrols2/material/qquickmaterialstyle_p.h
@@ -101,7 +101,7 @@ class QQuickMaterialStyle : public QQuickAttachedObject
Q_PROPERTY(QColor toolBarColor READ toolBarColor NOTIFY toolBarColorChanged FINAL)
Q_PROPERTY(QColor toolTextColor READ toolTextColor NOTIFY toolTextColorChanged FINAL)
Q_PROPERTY(QColor spinBoxDisabledIconColor READ spinBoxDisabledIconColor NOTIFY themeChanged FINAL)
- Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION 15)
+ Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION(2, 15))
Q_PROPERTY(int touchTarget READ touchTarget CONSTANT FINAL)
Q_PROPERTY(int buttonHeight READ buttonHeight CONSTANT FINAL)
diff --git a/src/quickcontrols2impl/qquickiconlabel.cpp b/src/quickcontrols2impl/qquickiconlabel.cpp
index a37ce02f6e..309a2c47d4 100644
--- a/src/quickcontrols2impl/qquickiconlabel.cpp
+++ b/src/quickcontrols2impl/qquickiconlabel.cpp
@@ -404,7 +404,7 @@ void QQuickIconLabel::setIcon(const QQuickIcon &icon)
return;
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
d->updateOrSyncImage();
}
diff --git a/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc b/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
index 92a25c3fd4..487d327497 100644
--- a/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
+++ b/src/quickdialogs2/quickdialogs2/doc/src/qtquickdialogs-index.qdoc
@@ -31,8 +31,8 @@
\brief Provides QML types for creating and interacting with system dialogs.
- The Qt Quick Dialogs module allows to create and interact with system dialogs
- from QML. The module was introduced in Qt 6.2.
+ The Qt Quick Dialogs module allows you to create and interact with system
+ dialogs from QML. The module was introduced in Qt 6.2.
\section1 Using the Module
diff --git a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
index 7e7f1fcf5c..011b1bb924 100644
--- a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
@@ -111,6 +111,7 @@ Q_LOGGING_CATEGORY(lcDialogs, "qt.quick.dialogs")
\brief The base class of native dialogs.
The Dialog type provides common QML API for native platform dialogs.
+ For the non-native dialog, see \l [QML QtQuickControls]{Dialog}.
To show a native dialog, construct an instance of one of the concrete
Dialog implementations, set the desired properties, and call \l open().
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
index b74ace9aca..da899140b7 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
@@ -581,6 +581,15 @@ QUrl QQuickFileDialog::addDefaultSuffix(const QUrl &file) const
return url;
}
+void QQuickFileDialog::accept()
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ // Take the currently selected files and make them the final set of files.
+ setSelectedFiles(fileDialog->selectedFiles());
+ }
+ QQuickAbstractDialog::accept();
+}
+
QList<QUrl> QQuickFileDialog::addDefaultSuffixes(const QList<QUrl> &files) const
{
QList<QUrl> urls;
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
index ef86e11133..1d1a0e82d1 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
@@ -146,6 +146,7 @@ protected:
void onCreate(QPlatformDialogHelper *dialog) override;
void onShow(QPlatformDialogHelper *dialog) override;
void onHide(QPlatformDialogHelper *dialog) override;
+ void accept() override;
private:
QUrl addDefaultSuffix(const QUrl &file) const;
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
index 4d0b90c3a7..54c17af2f2 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/MessageDialog.qml
@@ -55,7 +55,7 @@ MessageDialogImpl {
implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
control.contentHeight + control.topPadding + control.bottomPadding
+ (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + control.spacing : 0))
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
leftPadding: 20
rightPadding: 20
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
index 9d40f97431..cb58015ae3 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FontDialog.qml
@@ -149,7 +149,7 @@ FontDialogImpl {
Label {
text: qsTr("Writing System")
- Layout.leftMargin: 20
+ Layout.leftMargin: 16
Layout.bottomMargin: 16
}
ComboBox{
@@ -163,7 +163,7 @@ FontDialogImpl {
id: buttonBox
standardButtons: control.standardButtons
spacing: 12
- Layout.rightMargin: 20
+ Layout.rightMargin: 16
Layout.bottomMargin: 16
}
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
index e6d7b9606f..f8a2eec922 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/MessageDialog.qml
@@ -58,7 +58,7 @@ MessageDialogImpl {
implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
topPadding: background ? background.topPadding : 0
leftPadding: background ? background.leftPadding : 0
@@ -135,7 +135,7 @@ MessageDialogImpl {
objectName: "detailedTextButton"
text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
- Layout.leftMargin: 20
+ Layout.leftMargin: 16
}
DialogButtonBox {
@@ -146,9 +146,11 @@ MessageDialogImpl {
verticalPadding: 20
Layout.fillWidth: true
- Layout.leftMargin: detailedTextButton.visible ? 12 : 20
- Layout.rightMargin: 20
+ Layout.leftMargin: detailedTextButton.visible ? 12 : 16
+ Layout.rightMargin: 16
}
+
+ Layout.bottomMargin: 16
}
TextArea {
@@ -159,10 +161,12 @@ MessageDialogImpl {
wrapMode: TextEdit.WordWrap
readOnly: true
+ padding: 12
+
Layout.fillWidth: true
- Layout.leftMargin: 20
- Layout.rightMargin: 20
- Layout.bottomMargin: 20
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
background: Rectangle {
color: Qt.rgba(1,1,1,1)
@@ -170,7 +174,6 @@ MessageDialogImpl {
border.color: Qt.darker(control.palette.light)
border.width: 1
}
-
}
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
index 49c517d252..d1f7857e4b 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/MessageDialog.qml
@@ -54,7 +54,7 @@ MessageDialogImpl {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
leftPadding: 24
rightPadding: 24
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
index 40a06367bf..0be2a67184 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FontDialog.qml
@@ -83,14 +83,6 @@ FontDialogImpl {
border.width: 1 // FlyoutBorderThemeThickness
}
- Overlay.modal: Rectangle {
- color: control.Universal.baseLowColor
- }
-
- Overlay.modeless: Rectangle {
- color: control.Universal.baseLowColor
- }
-
header: Label {
text: control.title
elide: Label.ElideRight
@@ -144,4 +136,12 @@ FontDialogImpl {
Layout.rightMargin: 24
}
}
+
+ Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
index 7194a3a6aa..ca4d3f3cea 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/MessageDialog.qml
@@ -54,7 +54,7 @@ MessageDialogImpl {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + spacing : 0))
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
padding: 24
verticalPadding: 18
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
index 3bd1cdb88b..5a7bcc6112 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/MessageDialog.qml
@@ -53,7 +53,7 @@ MessageDialogImpl {
implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
control.contentHeight + control.topPadding + control.bottomPadding
+ (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
- + (columnLayout.implicitHeight > 0 ? columnLayout.implicitHeight + control.spacing : 0))
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
leftPadding: 20
rightPadding: 20
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
index 01fc90ee57..27f19aea5b 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
@@ -73,13 +73,7 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
break;
}
case QQuickDialogType::MessageDialog: {
- auto dialog = new QQuickPlatformMessageDialog(parent);
- if (!dialog->isValid()) {
- delete dialog;
- return nullptr;
- }
-
- dialogHelper.reset(dialog);
+ dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
break;
}
default:
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
index 64c624b5ec..a51c6bd3e6 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
@@ -41,6 +41,8 @@
#include "qquickfiledialogimpl_p_p.h"
#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
@@ -135,7 +137,15 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat
}
}
- if (newSelectedFilePath.isEmpty()) {
+ static const bool preselectFirstFile = []() {
+ const QVariant envVar = qEnvironmentVariable("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE");
+ if (envVar.isValid() && envVar.canConvert<bool>())
+ return envVar.toBool();
+ return QGuiApplicationPrivate::platformTheme()->themeHint(
+ QPlatformTheme::PreselectFirstFileInDirectory).toBool();
+ }();
+
+ if (preselectFirstFile && newSelectedFilePath.isEmpty()) {
// When entering into a directory that isn't a parent of the old one, the first
// file delegate should be selected.
// TODO: is there a cheaper way to do this? QDirIterator doesn't support sorting,
diff --git a/src/quicknativestyle/items/qquickstyleitem.h b/src/quicknativestyle/items/qquickstyleitem.h
index f65e5268bf..108f080c92 100644
--- a/src/quicknativestyle/items/qquickstyleitem.h
+++ b/src/quicknativestyle/items/qquickstyleitem.h
@@ -208,8 +208,8 @@ public:
NinePatchMargins = 0x100,
SaveImage = 0x200,
};
- Q_FLAG(DebugFlag)
Q_DECLARE_FLAGS(DebugFlags, DebugFlag)
+ Q_FLAG(DebugFlags)
#endif
explicit QQuickStyleItem(QQuickItem *parent = nullptr);
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp
index 191dd81b0b..7febf39c69 100644
--- a/src/quicktemplates2/qquickabstractbutton.cpp
+++ b/src/quicktemplates2/qquickabstractbutton.cpp
@@ -51,6 +51,7 @@
# include <QtGui/private/qshortcutmap_p.h>
#endif
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQml/qqmllist.h>
@@ -235,7 +236,8 @@ void QQuickAbstractButtonPrivate::handleUngrab()
bool QQuickAbstractButtonPrivate::acceptKeyClick(Qt::Key key) const
{
- return key == Qt::Key_Space;
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ return buttonPressKeys.contains(key);
}
bool QQuickAbstractButtonPrivate::isPressAndHoldConnected()
@@ -820,7 +822,7 @@ void QQuickAbstractButton::setIcon(const QQuickIcon &icon)
{
Q_D(QQuickAbstractButton);
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
d->updateEffectiveIcon();
}
diff --git a/src/quicktemplates2/qquickaction.cpp b/src/quicktemplates2/qquickaction.cpp
index ac2b449cd1..189bb860d9 100644
--- a/src/quicktemplates2/qquickaction.cpp
+++ b/src/quicktemplates2/qquickaction.cpp
@@ -406,7 +406,7 @@ void QQuickAction::setIcon(const QQuickIcon &icon)
return;
d->icon = icon;
- d->icon.setOwner(this);
+ d->icon.ensureRelativeSourceResolved(this);
emit iconChanged(icon);
}
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 85066b49f9..0ffecd6fc6 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -49,6 +49,7 @@
#include <QtCore/qglobal.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qqmlcontext.h>
@@ -1666,6 +1667,15 @@ QVariant QQuickComboBox::currentValue() const
return d->currentValue;
}
+/*!
+ \readonly
+ \since QtQuick.Controls 2.14 (Qt 5.14)
+ \qmlmethod var QtQuick.Controls::ComboBox::valueAt(int index)
+
+ Returns the value at position \a index in the combo box.
+
+ \sa indexOfValue
+*/
QVariant QQuickComboBox::valueAt(int index) const
{
Q_D(const QQuickComboBox);
@@ -1686,7 +1696,7 @@ QVariant QQuickComboBox::valueAt(int index) const
\include qquickcombobox.qdocinc functions-after-component-completion
- \sa find(), currentValue, currentIndex, valueRole
+ \sa find(), currentValue, currentIndex, valueRole, valueAt
*/
int QQuickComboBox::indexOfValue(const QVariant &value) const
{
@@ -1976,17 +1986,23 @@ void QQuickComboBox::keyPressEvent(QKeyEvent *event)
Q_D(QQuickComboBox);
QQuickControl::keyPressEvent(event);
- switch (event->key()) {
+ const auto key = event->key();
+ if (!isEditable()) {
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ if (buttonPressKeys.contains(key)) {
+ if (!event->isAutoRepeat())
+ setPressed(true);
+ event->accept();
+ return;
+ }
+ }
+
+ switch (key) {
case Qt::Key_Escape:
case Qt::Key_Back:
if (d->isPopupVisible())
event->accept();
break;
- case Qt::Key_Space:
- if (!event->isAutoRepeat())
- setPressed(true);
- event->accept();
- break;
case Qt::Key_Enter:
case Qt::Key_Return:
if (d->isPopupVisible())
@@ -2036,13 +2052,19 @@ void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
if (event->isAutoRepeat())
return;
- switch (event->key()) {
- case Qt::Key_Space:
- if (!isEditable())
- d->togglePopup(true);
- setPressed(false);
- event->accept();
- break;
+ const auto key = event->key();
+ if (!isEditable()) {
+ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
+ if (buttonPressKeys.contains(key)) {
+ if (!isEditable())
+ d->togglePopup(true);
+ setPressed(false);
+ event->accept();
+ return;
+ }
+ }
+
+ switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
if (!isEditable() || d->isPopupVisible())
diff --git a/src/quicktemplates2/qquickicon.cpp b/src/quicktemplates2/qquickicon.cpp
index 8b2f53f450..ca36de8edb 100644
--- a/src/quicktemplates2/qquickicon.cpp
+++ b/src/quicktemplates2/qquickicon.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qquickicon_p.h"
-#include "qtaggedpointer.h"
#include <private/qqmlcontextdata_p.h>
#include <private/qqmldata_p.h>
@@ -62,25 +61,11 @@ public:
QString name;
QUrl source;
+ QUrl resolvedSource;
int width = 0;
int height = 0;
QColor color = Qt::transparent;
-
- // we want DoCache as the default, and thus as the zero value
- // so that the tagged pointer can be zero initialized
- enum CacheStatus : bool { DoCache, SkipCaching };
- static_assert (DoCache == 0);
- /* We use a QTaggedPointer here to save space:
- - Without it, we would need an additional boolean, which due to
- alignment would increase the class size by sizeof(void *)
- - The pointer part stores the "owner" of the QQuickIcon, i.e.
- an object which has an icon property. We need the owner to
- access its context to resolve relative url's in the way users
- expect.
- - The tag bits are used to track whether caching is enabled.
- */
- QTaggedPointer<QObject, CacheStatus> ownerAndCache = nullptr;
-
+ bool cache = true;
};
QQuickIcon::QQuickIcon()
@@ -107,10 +92,11 @@ bool QQuickIcon::operator==(const QQuickIcon &other) const
{
return d == other.d || (d->name == other.d->name
&& d->source == other.d->source
+ && d->resolvedSource == other.d->resolvedSource
&& d->width == other.d->width
&& d->height == other.d->height
&& d->color == other.d->color
- && d->ownerAndCache == other.d->ownerAndCache);
+ && d->cache == other.d->cache);
}
bool QQuickIcon::operator!=(const QQuickIcon &other) const
@@ -157,6 +143,7 @@ void QQuickIcon::setSource(const QUrl &source)
d.detach();
d->source = source;
+ d->resolvedSource.clear();
d->resolveMask |= QQuickIconPrivate::SourceResolved;
}
@@ -164,18 +151,27 @@ void QQuickIcon::resetSource()
{
d.detach();
d->source = QString();
+ d->resolvedSource.clear();
d->resolveMask &= ~QQuickIconPrivate::SourceResolved;
}
QUrl QQuickIcon::resolvedSource() const
{
- if (QObject *owner = d->ownerAndCache.data()) {
- QQmlData *data = QQmlData::get(owner);
- if (data && data->outerContext)
- return data->outerContext->resolvedUrl(d->source);
- }
+ return d->resolvedSource.isEmpty() ? d->source : d->resolvedSource;
+}
- return d->source;
+// must be called by the property owner (e.g. Button) prior to emitting changed signal.
+void QQuickIcon::ensureRelativeSourceResolved(const QObject *owner)
+{
+ if (d->source.isEmpty())
+ return;
+ if (!d->resolvedSource.isEmpty())
+ return; // already resolved relative to (possibly) different owner
+ const QQmlData *data = QQmlData::get(owner);
+ if (!data || !data->outerContext)
+ return;
+ d.detach();
+ d->resolvedSource = data->outerContext->resolvedUrl(d->source);
}
int QQuickIcon::width() const
@@ -246,35 +242,26 @@ void QQuickIcon::resetColor()
bool QQuickIcon::cache() const
{
- return d->ownerAndCache.tag() == QQuickIconPrivate::DoCache;
+ return d->cache;
}
void QQuickIcon::setCache(bool cache)
{
- const auto cacheState = cache ? QQuickIconPrivate::DoCache : QQuickIconPrivate::SkipCaching;
- if ((d->resolveMask & QQuickIconPrivate::CacheResolved) && d->ownerAndCache.tag() == cacheState)
+ if ((d->resolveMask & QQuickIconPrivate::CacheResolved) && d->cache == cache)
return;
d.detach();
- d->ownerAndCache.setTag(cacheState);
+ d->cache = cache;
d->resolveMask |= QQuickIconPrivate::CacheResolved;
}
void QQuickIcon::resetCache()
{
d.detach();
- d->ownerAndCache.setTag(QQuickIconPrivate::DoCache);
+ d->cache = true;
d->resolveMask &= ~QQuickIconPrivate::CacheResolved;
}
-void QQuickIcon::setOwner(QObject *owner)
-{
- if (d->ownerAndCache.data() == owner)
- return;
- d.detach();
- d->ownerAndCache = owner;
-}
-
QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
{
QQuickIcon resolved = *this;
@@ -283,8 +270,10 @@ QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
if (!(d->resolveMask & QQuickIconPrivate::NameResolved))
resolved.d->name = other.d->name;
- if (!(d->resolveMask & QQuickIconPrivate::SourceResolved))
+ if (!(d->resolveMask & QQuickIconPrivate::SourceResolved)) {
resolved.d->source = other.d->source;
+ resolved.d->resolvedSource = other.d->resolvedSource;
+ }
if (!(d->resolveMask & QQuickIconPrivate::WidthResolved))
resolved.d->width = other.d->width;
@@ -296,9 +285,7 @@ QQuickIcon QQuickIcon::resolve(const QQuickIcon &other) const
resolved.d->color = other.d->color;
if (!(d->resolveMask & QQuickIconPrivate::CacheResolved))
- resolved.d->ownerAndCache.setTag(other.d->ownerAndCache.tag());
-
- // owner does not change when resolving an icon
+ resolved.d->cache = other.d->cache;
return resolved;
}
diff --git a/src/quicktemplates2/qquickicon_p.h b/src/quicktemplates2/qquickicon_p.h
index 13e52b956e..60a24a97be 100644
--- a/src/quicktemplates2/qquickicon_p.h
+++ b/src/quicktemplates2/qquickicon_p.h
@@ -94,6 +94,7 @@ public:
void setSource(const QUrl &source);
void resetSource();
QUrl resolvedSource() const;
+ void ensureRelativeSourceResolved(const QObject *owner);
int width() const;
void setWidth(int width);
@@ -111,10 +112,6 @@ public:
void setCache(bool cache);
void resetCache();
- // owner is not a property - it is set internally by classes using icon
- // so that we can resolve relative URL's correctly
- void setOwner(QObject *owner);
-
QQuickIcon resolve(const QQuickIcon &other) const;
private:
diff --git a/src/quicktemplates2/qquickmenuitem.cpp b/src/quicktemplates2/qquickmenuitem.cpp
index 5ba2d792d4..c8bd1c363c 100644
--- a/src/quicktemplates2/qquickmenuitem.cpp
+++ b/src/quicktemplates2/qquickmenuitem.cpp
@@ -152,7 +152,8 @@ void QQuickMenuItemPrivate::executeArrow(bool complete)
bool QQuickMenuItemPrivate::acceptKeyClick(Qt::Key key) const
{
- return key == Qt::Key_Space || key == Qt::Key_Return || key == Qt::Key_Enter;
+ return key == Qt::Key_Return || key == Qt::Key_Enter
+ || QQuickAbstractButtonPrivate::acceptKeyClick(key);
}
QPalette QQuickMenuItemPrivate::defaultPalette() const
diff --git a/src/quicktemplates2/qquickpresshandler.cpp b/src/quicktemplates2/qquickpresshandler.cpp
index 7e6bc384ef..451c426104 100644
--- a/src/quicktemplates2/qquickpresshandler.cpp
+++ b/src/quicktemplates2/qquickpresshandler.cpp
@@ -55,7 +55,8 @@ void QQuickPressHandler::mousePressEvent(QMouseEvent *event)
pressPos = event->position();
if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) {
timer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), control);
- delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->button(), event->buttons(), event->modifiers());
+ delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
+ event->button(), event->buttons(), event->modifiers());
} else {
timer.stop();
}
diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp
index 257722a4d3..14d4e3a86f 100644
--- a/src/quicktemplates2/qquickscrollbar.cpp
+++ b/src/quicktemplates2/qquickscrollbar.cpp
@@ -165,10 +165,12 @@ static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes |
QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
{
qreal visualPos = position;
- if (minimumSize > size)
+
+ if (minimumSize > size && size != 1.0)
visualPos = position / (1.0 - size) * (1.0 - minimumSize);
- qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos), 1.0 - visualPos);
+ qreal visualSize = qBound<qreal>(0, qMax(size, minimumSize) + qMin<qreal>(0, visualPos),
+ qMax(0.0, 1.0 - visualPos));
visualPos = qBound<qreal>(0, visualPos, qMax<qreal>(0, 1.0 - visualSize));
@@ -177,7 +179,7 @@ QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
qreal QQuickScrollBarPrivate::logicalPosition(qreal position) const
{
- if (minimumSize > size)
+ if (minimumSize > size && minimumSize != 1.0)
return position * (1.0 - size) / (1.0 - minimumSize);
return position;
}
@@ -433,11 +435,11 @@ qreal QQuickScrollBar::size() const
void QQuickScrollBar::setSize(qreal size)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->size, size))
+ if (!qt_is_finite(size) || qFuzzyCompare(d->size, size))
return;
auto oldVisualArea = d->visualArea();
- d->size = size;
+ d->size = qBound(0.0, size, 1.0);
if (isComponentComplete())
d->resizeContent();
emit sizeChanged();
@@ -465,7 +467,7 @@ qreal QQuickScrollBar::position() const
void QQuickScrollBar::setPosition(qreal position)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->position, position))
+ if (!qt_is_finite(position) || qFuzzyCompare(d->position, position))
return;
auto oldVisualArea = d->visualArea();
@@ -492,7 +494,7 @@ qreal QQuickScrollBar::stepSize() const
void QQuickScrollBar::setStepSize(qreal step)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->stepSize, step))
+ if (!qt_is_finite(step) || qFuzzyCompare(d->stepSize, step))
return;
d->stepSize = step;
@@ -736,11 +738,11 @@ qreal QQuickScrollBar::minimumSize() const
void QQuickScrollBar::setMinimumSize(qreal minimumSize)
{
Q_D(QQuickScrollBar);
- if (qFuzzyCompare(d->minimumSize, minimumSize))
+ if (!qt_is_finite(minimumSize) || qFuzzyCompare(d->minimumSize, minimumSize))
return;
auto oldVisualArea = d->visualArea();
- d->minimumSize = minimumSize;
+ d->minimumSize = qBound(0.0, minimumSize, 1.0);
if (isComponentComplete())
d->resizeContent();
emit minimumSizeChanged();
diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp
index dbf5738b77..d1990a4227 100644
--- a/src/quicktemplates2/qquickspinbox.cpp
+++ b/src/quicktemplates2/qquickspinbox.cpp
@@ -595,7 +595,8 @@ void QQuickSpinBox::setEditable(bool editable)
}
\endcode
- \sa editable, textFromValue, valueFromText, {Control::locale}{locale}
+ \sa editable, textFromValue, valueFromText, {Control::locale}{locale},
+ {Validating Input Text}
*/
QValidator *QQuickSpinBox::validator() const
{
diff --git a/src/quicktemplates2/qquicktreeviewdelegate.cpp b/src/quicktemplates2/qquicktreeviewdelegate.cpp
index b233020702..1636afbfa1 100644
--- a/src/quicktemplates2/qquicktreeviewdelegate.cpp
+++ b/src/quicktemplates2/qquicktreeviewdelegate.cpp
@@ -216,6 +216,13 @@ void QQuickTreeViewDelegate::mousePressEvent(QMouseEvent *event)
{
QQuickAbstractButton::mousePressEvent(event);
+ if (event->buttons() != Qt::LeftButton || event->modifiers() != Qt::NoModifier) {
+ // Allow application to add its own pointer handlers that does something
+ // other than plain expand/collapse if e.g holding down modifier keys.
+ event->ignore();
+ return;
+ }
+
const auto indicator = QQuickAbstractButton::indicator();
if (indicator && indicator->isVisible()) {
const auto posInIndicator = mapToItem(indicator, event->position());
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 37b3823bde..95c1be879c 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -187,6 +187,7 @@ void QQuickWidgetPrivate::initOffscreenWindow()
{
Q_Q(QQuickWidget);
offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl);
+ offscreenWindow->setScreen(q->screen());
// Do not call create() on offscreenWindow.
QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
@@ -558,19 +559,24 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which
will resize the view to the size of the root object.
- \note QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
+ \section1 Performance Considerations
+
+ QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
The restrictions on stacking order do not apply, making QQuickWidget the more flexible
alternative, behaving more like an ordinary widget.
- \note However, the above mentioned advantages come at the expense of performance.
- Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL
+ However, the above mentioned advantages come at the expense of performance:
+ \list
+
+ \li Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL
framebuffer objects, which needs to be enforced by calling
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi) at startup.
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL) at startup.
This will naturally carry a minor performance hit.
- \note Using QQuickWidget disables the threaded render loop on all platforms. This means that
- some of the benefits of threaded rendering, for example \l Animator classes and vsync driven
- animations, will not be available.
+ \li Using QQuickWidget disables the \l{threaded_render_loop}{threaded render loop} on all
+ platforms. This means that some of the benefits of threaded rendering, for example
+ \l Animator classes and vsync driven animations, will not be available.
+ \endlist
\note Avoid calling winId() on a QQuickWidget. This function triggers the creation of
a native window, resulting in reduced performance and possibly rendering glitches. The
@@ -1035,9 +1041,7 @@ void QQuickWidgetPrivate::createContext()
context = new QOpenGLContext;
context->setFormat(offscreenWindow->requestedFormat());
- const QWindow *win = q->window()->windowHandle();
- if (win && win->screen())
- context->setScreen(win->screen());
+ context->setScreen(q->screen());
QOpenGLContext *shareContext = qt_gl_global_share_context();
if (!shareContext)
shareContext = QWidgetPrivate::get(q->window())->shareContext();
@@ -1657,19 +1661,16 @@ bool QQuickWidget::event(QEvent *e)
d->handleWindowChange();
break;
- case QEvent::ScreenChangeInternal:
- if (QWindow *window = this->window()->windowHandle()) {
- QScreen *newScreen = window->screen();
-
- if (d->offscreenWindow)
- d->offscreenWindow->setScreen(newScreen);
- if (d->offscreenSurface)
- d->offscreenSurface->setScreen(newScreen);
+ case QEvent::ScreenChangeInternal: {
+ QScreen *newScreen = screen();
+ if (d->offscreenWindow)
+ d->offscreenWindow->setScreen(newScreen);
+ if (d->offscreenSurface)
+ d->offscreenSurface->setScreen(newScreen);
#if QT_CONFIG(opengl)
- if (d->context)
- d->context->setScreen(newScreen);
+ if (d->context)
+ d->context->setScreen(newScreen);
#endif
- }
if (d->useSoftwareRenderer
#if QT_CONFIG(opengl)
@@ -1682,7 +1683,7 @@ bool QQuickWidget::event(QEvent *e)
d->render(true);
}
break;
-
+ }
case QEvent::Show:
case QEvent::Move:
d->updatePosition();
diff --git a/sync.profile b/sync.profile
index 8134c21328..d7c15bc6d1 100644
--- a/sync.profile
+++ b/sync.profile
@@ -13,7 +13,6 @@
"QtQmlModels" => "$basedir/src/qmlmodels",
"QtQmlWorkerScript" => "$basedir/src/qmlworkerscript",
"QtQmlCompiler" => "$basedir/src/qmlcompiler",
- "QtQmlLint" => "$basedir/src/qmllint",
"QtQmlDom" => "$basedir/src/qmldom",
"QtQuickLayouts" => "$basedir/src/quicklayouts",
"QtQmlLocalStorage" => "$basedir/src/qmllocalstorage",
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index f2d0ca547a..3ef1b6e735 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -23,7 +23,7 @@ endif()
if(TARGET Qt::QuickWidgets)
add_subdirectory(quickwidgets)
endif()
-if(TARGET Qt::QmlDomPrivate)
+if(TARGET Qt::QmlDomPrivate AND NOT CMAKE_CROSSCOMPILING)
add_subdirectory(qmldom)
add_subdirectory(qmlls)
endif()
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 1d75a0bdba..4ea17fb0eb 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -65,7 +65,9 @@ if(TARGET Qt::Qml)
_qt_internal_test_expect_pass(test_plugins)
endif()
_qt_internal_test_expect_pass(empty_qmldir)
- _qt_internal_test_expect_pass(qmlquery)
+ If(NOT ANDROID) # QML only project cannot run on Android with C++ enty point
+ _qt_internal_test_expect_pass(qmlquery)
+ endif()
endif()
if(TARGET Qt::Quick)
diff --git a/tests/auto/cmake/empty_qmldir/CMakeLists.txt b/tests/auto/cmake/empty_qmldir/CMakeLists.txt
index 432e6e99da..259adc2610 100644
--- a/tests/auto/cmake/empty_qmldir/CMakeLists.txt
+++ b/tests/auto/cmake/empty_qmldir/CMakeLists.txt
@@ -14,6 +14,11 @@ endif()
qt_add_executable(empty_qmldir_test main.cpp)
target_link_libraries(empty_qmldir_test PRIVATE Qt6::Test)
+if(ANDROID)
+ # Tests link to Gui by default beacuse it's needed for Android (QTBUG-83997) when
+ # using qt_internal_add_test() but not here where qt_add_executable() is used.
+ target_link_libraries(empty_qmldir_test PRIVATE Qt6::Gui)
+endif()
qt_add_qml_module(empty_qmldir_test
URI Third
diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt
index 31413de9c4..a57498db91 100644
--- a/tests/auto/core/CMakeLists.txt
+++ b/tests/auto/core/CMakeLists.txt
@@ -14,3 +14,7 @@ qt_internal_add_test(tst_core
Qt::Qml
TESTDATA ${test_data}
)
+
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_core)
+endif()
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 4c61f59736..fd587e93a6 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -32,7 +32,12 @@ add_subdirectory(qtqmlmodules)
add_subdirectory(qquickfolderlistmodel)
add_subdirectory(qqmlapplicationengine)
add_subdirectory(qqmlsettings)
-add_subdirectory(qmldiskcache)
+
+if(NOT INTEGRITY)
+# There's no mounted filesystem on INTEGRITY therefore skipping qmldiskcache
+ add_subdirectory(qmldiskcache)
+endif()
+
add_subdirectory(qqmlmetatype)
if(TARGET Qt::Quick)
add_subdirectory(qmltc_manual)
@@ -44,7 +49,9 @@ if(TARGET Qt::Quick)
(QT_BUILD_STANDALONE_TESTS OR QT6_IS_SHARED_LIBS_BUILD))
add_subdirectory(qmlbasicapp)
endif()
- add_subdirectory(qqmljsscope)
+ if(NOT CMAKE_CROSSCOMPILING)
+ add_subdirectory(qqmljsscope)
+ endif()
endif()
add_subdirectory(qmlsplitlib)
if(TARGET Qt::Widgets)
diff --git a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml
index b9a196e188..63d33cbea6 100644
--- a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml
+++ b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
-// code in tst_parserstress.cpp
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQuick 2.0
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
index 69bb86ef09..2c7d2ed9f5 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -1075,7 +1075,7 @@ void tst_QQmlDebugJS::letConstLocals()
const auto value = m_client->response();
if (value.command == QStringLiteral("frame")) {
const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray();
- for (const QJsonValueRef scope : scopes) {
+ for (const auto scope : scopes) {
const auto scopeObject = scope.toObject();
const int type = scopeObject.value("type").toInt();
if (type == 1 || type == 4) {
@@ -1087,7 +1087,7 @@ void tst_QQmlDebugJS::letConstLocals()
} else if (value.command == QStringLiteral("scope")) {
const auto props = value.body.toObject().value(QStringLiteral("object")).toObject()
.value(QStringLiteral("properties")).toArray();
- for (const QJsonValueRef prop : props) {
+ for (const auto prop : props) {
const auto propObj = prop.toObject();
QString name = propObj.value(QStringLiteral("name")).toString();
QVERIFY(name.length() == 1);
diff --git a/tests/auto/qml/parserstress/CMakeLists.txt b/tests/auto/qml/parserstress/CMakeLists.txt
index 2d2f3df4c3..4505dab1da 100644
--- a/tests/auto/qml/parserstress/CMakeLists.txt
+++ b/tests/auto/qml/parserstress/CMakeLists.txt
@@ -18,8 +18,12 @@ qt_internal_add_test(tst_parserstress
Qt::Gui
Qt::GuiPrivate
Qt::QmlPrivate
- TESTDATA ${test_data}
+ TESTDATA ${test_data} "dummy_imports.qml"
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_parserstress)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/parserstress/dummy_imports.qml b/tests/auto/qml/parserstress/dummy_imports.qml
index b9a196e188..63d33cbea6 100644
--- a/tests/auto/qml/parserstress/dummy_imports.qml
+++ b/tests/auto/qml/parserstress/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
-// code in tst_parserstress.cpp
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQuick 2.0
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qjsengine/CMakeLists.txt b/tests/auto/qml/qjsengine/CMakeLists.txt
index 6dc74b8858..3e8939a546 100644
--- a/tests/auto/qml/qjsengine/CMakeLists.txt
+++ b/tests/auto/qml/qjsengine/CMakeLists.txt
@@ -27,7 +27,7 @@ qt_internal_add_test(tst_qjsengine
Qt::QmlPrivate
LIBRARIES # special case
Threads::Threads # special case
- TESTDATA ${test_data}
+ TESTDATA ${test_data} "dummy_imports.qml"
)
# Resources:
@@ -61,6 +61,10 @@ qt_internal_add_resource(tst_qjsengine "qmake_immediate"
${qmake_immediate_resource_files}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qjsengine)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qjsengine/dummy_imports.qml b/tests/auto/qml/qjsengine/dummy_imports.qml
index 8d86f3583b..afe2b33adf 100644
--- a/tests/auto/qml/qjsengine/dummy_imports.qml
+++ b/tests/auto/qml/qjsengine/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
-// code in tst_parserstress.cpp
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQml 2.0
+import QtQml
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index a92141a1e5..6ffc1da3a5 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -69,6 +69,8 @@ private slots:
void newArray_HooliganTask233836();
void toScriptValueBuiltin_data();
void toScriptValueBuiltin();
+ void toScriptValueQmlBuiltin_data();
+ void toScriptValueQmlBuiltin();
void toScriptValueQtQml_data();
void toScriptValueQtQml();
void toScriptValuenotroundtripped_data();
@@ -580,6 +582,36 @@ void tst_QJSEngine::toScriptValueBuiltin()
QCOMPARE(input, output);
}
+void tst_QJSEngine::toScriptValueQmlBuiltin_data()
+{
+ QTest::addColumn<QVariant>("input");
+
+ QTest::newRow("QList<QVariant>") << QVariant(QList<QVariant>{true, 5, 13.2f, 42.24, QString("world"), QUrl("htt://a.com"), QDateTime::currentDateTime(), QRegularExpression("a*b*c"), QByteArray("hello")});
+ QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false});
+ QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>{1, 2, 3, 4});
+ QTest::newRow("QList<float>") << QVariant::fromValue(QList<float>{1.1f, 2.2f, 3.3f, 4.4f});
+ QTest::newRow("QList<double>") << QVariant::fromValue(QList<double>{1.1, 2.2, 3.3, 4.4});
+ QTest::newRow("QList<QString>") << QVariant::fromValue(QList<QString>{"a", "b", "c", "d"});
+ QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
+ QTest::newRow("QList<QDateTime>") << QVariant::fromValue(QList<QDateTime>{QDateTime::currentDateTime(), QDateTime::fromMSecsSinceEpoch(300), QDateTime()});
+ QTest::newRow("QList<QRegularExpression>") << QVariant::fromValue(QList<QRegularExpression>{QRegularExpression("abcd"), QRegularExpression("a[b|c]d$"), QRegularExpression("a*b*d")});
+ QTest::newRow("QList<QByteArray>") << QVariant::fromValue(QList<QByteArray>{QByteArray("aaa"), QByteArray("bbb"), QByteArray("ccc")});
+}
+
+void tst_QJSEngine::toScriptValueQmlBuiltin()
+{
+ QFETCH(QVariant, input);
+
+ // We need the type registrations in QQmlEngine::init() for this.
+ QQmlEngine engine;
+
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant output = engine.fromScriptValue<QVariant>(outputJS);
+
+ QVERIFY(output.convert(input.metaType()));
+ QCOMPARE(input, output);
+}
+
void tst_QJSEngine::toScriptValueQtQml_data()
{
QTest::addColumn<QVariant>("input");
@@ -592,10 +624,6 @@ void tst_QJSEngine::toScriptValueQtQml_data()
QTest::newRow("std::vector<QString>") << QVariant::fromValue(std::vector<QString>{"a", "b", "c", "d"});
QTest::newRow("std::vector<QUrl>") << QVariant::fromValue(std::vector<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
- QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>{1, 2, 3, 4});
- QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false});
- QTest::newRow("QStringList") << QVariant::fromValue(QStringList{"a", "b", "c", "d"});
- QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
static const QStandardItemModel model(4, 4);
@@ -5325,7 +5353,7 @@ void tst_QJSEngine::typedArraySet()
QJSEngine engine;
const auto value = engine.evaluate(
"(function() {"
- " var length = 0xffffffe;"
+ " var length = 0xfffffe0;"
" var offset = 0xfffffff0;"
" var e1;"
" var e2;"
diff --git a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
index 3e466d8e2e..2449b9a320 100644
--- a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
+++ b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
@@ -10,7 +10,12 @@ qt_internal_add_test(tst_qjsmanagedvalue
PUBLIC_LIBRARIES
Qt::Qml
Qt::QmlPrivate
+ TESTDATA "dummy_imports.qml"
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qjsmanagedvalue)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml b/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml
index 1cc20b8aba..afe2b33adf 100644
--- a/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml
+++ b/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml
@@ -1,7 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
+// Otherwise, it fails to find the imports that are expressed in C++.
import QtQml
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qjsvalue/CMakeLists.txt b/tests/auto/qml/qjsvalue/CMakeLists.txt
index bb00f3221a..b8ee79713c 100644
--- a/tests/auto/qml/qjsvalue/CMakeLists.txt
+++ b/tests/auto/qml/qjsvalue/CMakeLists.txt
@@ -22,7 +22,12 @@ qt_internal_add_test(tst_qjsvalue
Qt::Widgets
LIBRARIES # special case
Threads::Threads # special case
+ TESTDATA "dummy_imports.qml"
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qjsvalue)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qjsvalue/dummy_imports.qml b/tests/auto/qml/qjsvalue/dummy_imports.qml
index 1cc20b8aba..afe2b33adf 100644
--- a/tests/auto/qml/qjsvalue/dummy_imports.qml
+++ b/tests/auto/qml/qjsvalue/dummy_imports.qml
@@ -1,7 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
+// Otherwise, it fails to find the imports that are expressed in C++.
import QtQml
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
index 2fd7ef634b..a6c6e4accd 100644
--- a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
+++ b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
@@ -44,6 +44,9 @@ private slots:
void tst_basicapp::loadComponent()
{
QQmlEngine engine;
+#ifdef Q_OS_ANDROID
+ engine.addImportPath(":/");
+#endif
QQmlComponent c(&engine, QStringLiteral("qrc:/BasicApp/main.qml"));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer o(c.create());
@@ -83,6 +86,9 @@ void tst_basicapp::resourceFiles()
void tst_basicapp::fileSystemFiles()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("This test is not valid for Android, because the files can exist only as resources.");
+#endif
const QString basedir = QCoreApplication::applicationDirPath();
QVERIFY(QFile::exists(basedir + QStringLiteral("/BasicApp/main.qml")));
QVERIFY(QFile::exists(basedir + QStringLiteral("/BasicApp/qmldir")));
@@ -99,8 +105,13 @@ void tst_basicapp::fileSystemFiles()
void tst_basicapp::qmldirContents()
{
+#ifdef Q_OS_ANDROID
+ const QString basedir = QStringLiteral(":"); // Use qrc resource path on Android
+#else
+ const QString basedir = QCoreApplication::applicationDirPath();
+#endif
{
- QFile qmldir(QCoreApplication::applicationDirPath() + "/BasicApp/qmldir");
+ QFile qmldir(basedir + "/BasicApp/qmldir");
QVERIFY(qmldir.open(QIODevice::ReadOnly));
const QByteArray contents = qmldir.readAll();
QVERIFY(contents.contains("module BasicApp"));
@@ -115,7 +126,7 @@ void tst_basicapp::qmldirContents()
}
{
- QFile qmldir(QCoreApplication::applicationDirPath() + "/TimeExample/qmldir");
+ QFile qmldir(basedir + "/TimeExample/qmldir");
QVERIFY(qmldir.open(QIODevice::ReadOnly));
const QByteArray contents = qmldir.readAll();
QVERIFY(contents.contains("module TimeExample"));
@@ -132,7 +143,7 @@ void tst_basicapp::qmldirContents()
}
{
- QFile qmldir(QCoreApplication::applicationDirPath() + "/BasicExtension/qmldir");
+ QFile qmldir(basedir + "/BasicExtension/qmldir");
QVERIFY(qmldir.open(QIODevice::ReadOnly));
const QByteArray contents = qmldir.readAll();
QVERIFY(contents.contains("More 1.0 More.ui.qml"));
diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
index 4c4cf0b1e5..946fa38e44 100644
--- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
@@ -4,7 +4,7 @@ qt_internal_add_test(tst_qmlcppcodegen
SOURCES
tst_qmlcppcodegen.cpp
LIBRARIES
- Qt::Qml
+ Qt::QmlPrivate
Qt::Gui
codegen_test_module
codegen_test_moduleplugin
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 053c3bccc2..bdfe81a1ac 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -1,6 +1,7 @@
set(cpp_sources
birthdayparty.cpp birthdayparty.h
cppbaseclass.h
+ dynamicmeta.h
objectwithmethod.h
person.cpp person.h
theme.cpp theme.h
@@ -35,6 +36,7 @@ set(qml_files
asCast.qml
attachedBaseEnum.qml
bindToValueType.qml
+ blockComments.qml
callContextPropertyLookupResult.qml
childobject.qml
colorAsVariant.qml
@@ -49,6 +51,7 @@ set(qml_files
curlygrouped.qml
cycleHead.qml
deadShoeSize.qml
+ deadStoreLoop.qml
dialog.qml
dynamicscene.qml
enumInvalid.qml
@@ -59,19 +62,24 @@ set(qml_files
excessiveParameters.qml
extendedTypes.qml
failures.qml
+ fallbacklookups.qml
fileDialog.qml
+ functionLookup.qml
funcWithParams.qml
functionReturningVoid.qml
+ functionTakingVar.qml
globals.qml
idAccess.qml
immediateQuit.qml
imports/QmlBench/Globals.qml
importsFromImportPath.qml
+ infinities.qml
invisibleBase.qml
intEnumCompare.qml
intOverflow.qml
interactive.qml
interceptor.qml
+ isnan.qml
jsMathObject.qml
jsimport.qml
jsmoduleimport.qml
@@ -82,12 +90,14 @@ set(qml_files
math.qml
methods.qml
modulePrefix.qml
+ moveRegVoid.qml
noBindingLoop.qml
noQQmlData.qml
nonNotifyable.qml
noscope.qml
notEqualsInt.qml
nullAccess.qml
+ objectInVar.qml
outOfBounds.qml
overriddenMember.qml
ownProperty.qml
@@ -110,6 +120,7 @@ set(qml_files
text.qml
themerbad.qml
themergood.qml
+ typedArray.qml
undefinedResets.qml
unknownAttached.qml
unknownParameter.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/blockComments.qml b/tests/auto/qml/qmlcppcodegen/data/blockComments.qml
new file mode 100644
index 0000000000..da4bb2fd25
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/blockComments.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property real implicitHeight: {
+ return /*+ (control.implicitHeaderHeight > 0
+ ? control.implicitHeaderHeight + control.spacing
+ : 0)*/ 8;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
index e7bde6c3e6..c3a9414ae2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
@@ -5,6 +5,10 @@ Item {
id: spy
visible: false
+ Rectangle {
+ id: rect
+ }
+
TestUtil {
id: util
}
@@ -15,6 +19,10 @@ Item {
readonly property alias valid:spy.qtest_valid
readonly property alias signalArguments:spy.qtest_signalArguments
+ function clearRectGradient() {
+ rect.gradient = null;
+ }
+
function clear() {
qtest_count = 0
qtest_expectedCount = 0
diff --git a/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml b/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml
new file mode 100644
index 0000000000..f493e4b942
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ Menu {
+ id: m
+ }
+ function c() {
+ while (m.count > 0) {
+ m.removeItem(m.itemAt(0))
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
new file mode 100644
index 0000000000..3f02e460e7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
@@ -0,0 +1,78 @@
+#ifndef DYNAMICMETA_H
+#define DYNAMICMETA_H
+
+#include <private/qobject_p.h>
+#include <QtQmlIntegration/qqmlintegration.h>
+
+struct FreeDeleter {
+ void operator()(QMetaObject *meta) { free(meta); }
+};
+
+template<typename T>
+class MetaObjectData : public QDynamicMetaObjectData
+{
+ Q_DISABLE_COPY_MOVE(MetaObjectData)
+public:
+ MetaObjectData() = default;
+ ~MetaObjectData() = default;
+
+ QMetaObject *toDynamicMetaObject(QObject *) override
+ {
+ return const_cast<QMetaObject *>(&T::staticMetaObject);
+ }
+ int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override
+ {
+ return o->qt_metacall(call, idx, argv);
+ }
+};
+
+class DynamicMeta : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL)
+ QML_ELEMENT
+public:
+
+ DynamicMeta(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ // deletes itself
+ QObjectPrivate::get(this)->metaObject = new MetaObjectData<DynamicMeta>;
+ }
+
+ int foo() const { return m_foo; }
+ void setFoo(int newFoo)
+ {
+ if (m_foo != newFoo) {
+ m_foo = newFoo;
+ emit fooChanged();
+ }
+ }
+
+ Q_INVOKABLE int bar(int baz) { return baz + 12; }
+
+Q_SIGNALS:
+ void fooChanged();
+
+private:
+ int m_foo = 0;
+};
+
+class DynamicMetaSingleton : public DynamicMeta
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(DynamicMetaSingleton *itself READ itself CONSTANT FINAL)
+public:
+ DynamicMetaSingleton(QObject *parent = nullptr) : DynamicMeta(parent)
+ {
+ QObjectPrivate *d = QObjectPrivate::get(this);
+ delete d->metaObject;
+ d->metaObject = new MetaObjectData<DynamicMetaSingleton>;
+ }
+
+ DynamicMetaSingleton *itself() { return this; }
+};
+
+#endif // DYNAMICMETA_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml
new file mode 100644
index 0000000000..4b58cd344d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml
@@ -0,0 +1,34 @@
+import TestTypes
+import QtQml
+
+DynamicMeta {
+ id: self
+
+ function getSingleton(): QtObject {
+ return DynamicMetaSingleton.itself
+ }
+
+ function withContext(): int {
+ foo = 93;
+ objectName = "aa" + foo;
+ return bar(4);
+ }
+
+ function withId(): int {
+ self.foo = 94;
+ self.objectName = "bb" + foo;
+ return self.bar(5);
+ }
+
+ function withSingleton(): int {
+ DynamicMetaSingleton.foo = 95;
+ DynamicMetaSingleton.objectName = "cc" + DynamicMetaSingleton.foo;
+ return DynamicMetaSingleton.bar(6);
+ }
+
+ function withProperty(): int {
+ DynamicMetaSingleton.itself.foo = 96;
+ DynamicMetaSingleton.itself.objectName = "dd" + DynamicMetaSingleton.itself.foo;
+ return DynamicMetaSingleton.itself.bar(7);
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml b/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml
new file mode 100644
index 0000000000..211f524088
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ function foo() { return "a" + 99 }
+ property var bar: foo
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
new file mode 100644
index 0000000000..1765bfcab9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property var c;
+
+ function a(b: var) {
+ c = b;
+ }
+
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/infinities.qml b/tests/auto/qml/qmlcppcodegen/data/infinities.qml
new file mode 100644
index 0000000000..5813d7a3ac
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/infinities.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ property real positiveInfinity: Infinity
+ property real negativeInfinity: -Infinity
+ property real positiveZero: 0.0
+ property real negativeZero: -0.0
+ property real naN: NaN
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/isnan.qml b/tests/auto/qml/qmlcppcodegen/data/isnan.qml
new file mode 100644
index 0000000000..dfc64e8002
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/isnan.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ property real good: 10.1
+ property real bad: "f" / 10
+
+ property bool a: isNaN(good)
+ property bool b: isNaN(bad)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml b/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml
new file mode 100644
index 0000000000..f1e78babba
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Rectangle {
+ id: root
+ property bool translucency: false
+
+ gradient: Gradient {
+ id: grad
+ }
+
+ onTranslucencyChanged: {
+ if (translucency) {
+ root.color = "transparent";
+ root.gradient = null;
+ } else {
+ root.color = "white";
+ root.gradient = grad;
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml b/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml
new file mode 100644
index 0000000000..9177ff2089
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+
+ property var thing: self
+
+ function doThing() : bool {
+ if (self.thing)
+ return true;
+ else
+ return false;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml
new file mode 100644
index 0000000000..0072357ae3
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<bool> values1: []
+ property list<int> values2: []
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 99376491fd..74794eb7fc 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -23,6 +23,8 @@
#include <data/cppbaseclass.h>
#include <data/objectwithmethod.h>
+#include <QtQml/private/qqmlengine_p.h>
+
#include <QtTest>
#include <QtQml>
#include <QtGui/qcolor.h>
@@ -117,6 +119,13 @@ private slots:
void revisions();
void invisibleBase();
void notEqualsInt();
+ void infinities();
+ void blockComments();
+ void functionLookup();
+ void objectInVar();
+ void functionTakingVar();
+ void testIsnan();
+ void fallbackLookups();
};
void tst_QmlCppCodegen::simpleBinding()
@@ -646,6 +655,8 @@ void tst_QmlCppCodegen::interestingFiles_data()
QTest::addRow("dynamicscene") << u"dynamicscene.qml"_qs << true;
QTest::addRow("curlygrouped") << u"curlygrouped.qml"_qs << true;
QTest::addRow("cycleHead") << u"cycleHead.qml"_qs << false;
+ QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_qs << true;
+ QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_qs << true;
}
void tst_QmlCppCodegen::interestingFiles()
@@ -1753,6 +1764,146 @@ void tst_QmlCppCodegen::notEqualsInt()
QCOMPARE(t->property("text").toString(), u"Bar"_qs);
}
+void tst_QmlCppCodegen::infinities()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/infinities.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity());
+ QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity());
+
+ const double positiveZero = o->property("positiveZero").toDouble();
+ QCOMPARE(positiveZero, 0.0);
+ QVERIFY(!std::signbit(positiveZero));
+
+ const double negativeZero = o->property("negativeZero").toDouble();
+ QCOMPARE(negativeZero, -0.0);
+ QVERIFY(std::signbit(negativeZero));
+
+ QVERIFY(qIsNaN(o->property("naN").toDouble()));
+}
+
+void tst_QmlCppCodegen::blockComments()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/blockComments.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("implicitHeight").toDouble(), 8.0);
+}
+
+void tst_QmlCppCodegen::functionLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/functionLookup.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ const QVariant foo = o->property("bar");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>());
+ const QJSManagedValue method(engine.toScriptValue(foo), &engine);
+ QVERIFY(method.isFunction());
+ const QJSValue result = method.call();
+ QVERIFY(result.isString());
+ QCOMPARE(result.toString(), QStringLiteral("a99"));
+}
+
+void tst_QmlCppCodegen::objectInVar()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/objectInVar.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
+
+ bool result = false;
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(result);
+
+ o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(!result);
+}
+
+void tst_QmlCppCodegen::functionTakingVar()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/functionTakingVar.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(!o->property("c").isValid());
+
+ int value = 11;
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine);
+ void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) };
+ QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() };
+ e->executeRuntimeFunction(document, 0, o.data(), 1, args, types);
+
+ QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
+}
+
+void tst_QmlCppCodegen::testIsnan()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/isnan.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("good").toDouble(), 10.1);
+ QVERIFY(qIsNaN(o->property("bad").toDouble()));
+
+ const QVariant a = o->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!a.toBool());
+
+ const QVariant b = o->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(b.toBool());
+}
+
+void tst_QmlCppCodegen::fallbackLookups()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/fallbacklookups.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->objectName(), QString());
+ int result = 0;
+
+ QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 16);
+ QCOMPARE(o->objectName(), QStringLiteral("aa93"));
+
+ QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 17);
+ QCOMPARE(o->objectName(), QStringLiteral("bb94"));
+
+ QObject *singleton = nullptr;
+ QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton));
+ QVERIFY(singleton);
+
+ QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 18);
+ QCOMPARE(singleton->objectName(), QStringLiteral("cc95"));
+
+ QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 19);
+ QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
+}
+
void tst_QmlCppCodegen::runInterpreted()
{
if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
diff --git a/tests/auto/qml/qmldiskcache/dummy_imports.qml b/tests/auto/qml/qmldiskcache/dummy_imports.qml
index b9a196e188..63d33cbea6 100644
--- a/tests/auto/qml/qmldiskcache/dummy_imports.qml
+++ b/tests/auto/qml/qmldiskcache/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in C++
-// code in tst_parserstress.cpp
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQuick 2.0
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qmlformat/BLACKLIST b/tests/auto/qml/qmlformat/BLACKLIST
index 4f2b3f7272..8b904fd665 100644
--- a/tests/auto/qml/qmlformat/BLACKLIST
+++ b/tests/auto/qml/qmlformat/BLACKLIST
@@ -1,3 +1,3 @@
-# QTBUG-09404
+# QTBUG-98494
[testExample]
-*
+windows-10 gcc
diff --git a/tests/auto/qml/qmllint/data/Things/qmldir b/tests/auto/qml/qmllint/data/Things/qmldir
index 8902de116b..e1d493a480 100644
--- a/tests/auto/qml/qmllint/data/Things/qmldir
+++ b/tests/auto/qml/qmllint/data/Things/qmldir
@@ -3,4 +3,4 @@ Something 1.0 SomethingElse.qml
typeinfo plugins.qmltypes
depends QtQuick 2.0
import QtQml 2.0
-optional import QtQuick.LocalStorage auto
+default import QtQuick.LocalStorage auto
diff --git a/tests/auto/qml/qmllint/data/callJSValueProp.qml b/tests/auto/qml/qmllint/data/callJSValueProp.qml
new file mode 100644
index 0000000000..357c810383
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/callJSValueProp.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ function foo() {}
+ Component.onCompleted: Qt.callLater(foo);
+}
diff --git a/tests/auto/qml/qmllint/data/callVarProp.qml b/tests/auto/qml/qmllint/data/callVarProp.qml
new file mode 100644
index 0000000000..ad69e2a679
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/callVarProp.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ property var foo: () => {}
+
+ Component.onCompleted: foo()
+}
diff --git a/tests/auto/qml/qmllint/data/optionalImport.qml b/tests/auto/qml/qmllint/data/defaultImport.qml
index 2be7cd444a..2be7cd444a 100644
--- a/tests/auto/qml/qmllint/data/optionalImport.qml
+++ b/tests/auto/qml/qmllint/data/defaultImport.qml
diff --git a/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml b/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml
new file mode 100644
index 0000000000..142134d49e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml
@@ -0,0 +1,6 @@
+import Things 1.0
+
+SomethingEntirelyStrange {
+ id: self
+ property var a: palette
+}
diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml
new file mode 100644
index 0000000000..ca8924f3f3
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property string quote: "
+ quote: \" \\\" \\\\\"
+ ticks: ` \` \\\` \\\`
+ singleTicks: ' \' \\' \\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ "
+}
diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml
new file mode 100644
index 0000000000..40484603d0
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property string quote: '
+ quote: " \" \\" \\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: \' \\\' \\\\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ '
+}
diff --git a/tests/auto/qml/qmllint/data/qmodelIndex.qml b/tests/auto/qml/qmllint/data/qmodelIndex.qml
new file mode 100644
index 0000000000..e0df84b68b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/qmodelIndex.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property ItemSelectionModel itemSelectionModel;
+ function row() { return itemSelectionModel.currentIndex.row; }
+}
diff --git a/tests/auto/qml/qmllint/data/shadowedMethod.qml b/tests/auto/qml/qmllint/data/shadowedMethod.qml
new file mode 100644
index 0000000000..72f18aaec7
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/shadowedMethod.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+QtObject {
+ function foo() {}
+ property bool foo: false
+
+ Component.onCompleted: foo()
+}
diff --git a/tests/auto/qml/qmllint/data/shadowedSignal.qml b/tests/auto/qml/qmllint/data/shadowedSignal.qml
new file mode 100644
index 0000000000..e4bb003495
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/shadowedSignal.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+MouseArea {
+ id: mouseArea
+ Component.onCompleted: pressed()
+}
diff --git a/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml b/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml
new file mode 100644
index 0000000000..ed7cc9f6c4
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+MouseArea {
+ id: mouseArea
+ Component.onCompleted: mouseArea.pressed()
+}
diff --git a/tests/auto/qml/qmllint/data/shadowedSlot.qml b/tests/auto/qml/qmllint/data/shadowedSlot.qml
new file mode 100644
index 0000000000..cb09645746
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/shadowedSlot.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+ObjectModel {
+ property bool move: false
+ Component.onCompleted: move()
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 2469228dc1..32065c0246 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -40,6 +40,28 @@ class TestQmllint: public QQmlDataTest
public:
TestQmllint();
+ struct Message
+ {
+ QString text = QString();
+ quint32 line = 0, column = 0;
+ QtMsgType severity = QtWarningMsg;
+ };
+
+ struct Result
+ {
+ enum Flag { ExitsNormally = 0x1, NoMessages = 0x2 };
+
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ static Result clean() { return Result { {}, {}, {}, { NoMessages, ExitsNormally } }; }
+
+ QList<Message> expectedMessages = {};
+ QList<Message> badMessages = {};
+ QList<Message> expectedReplacements = {};
+
+ Flags flags = {};
+ };
+
private Q_SLOTS:
void initTestCase() override;
@@ -96,20 +118,37 @@ private:
QString runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult,
const QStringList &extraArgs = QStringList(), bool ignoreSettings = true,
- bool addImportDirs = true);
+ bool addImportDirs = true, bool absolutePath = true);
QString runQmllint(const QString &fileToLint, bool shouldSucceed,
const QStringList &extraArgs = QStringList(), bool ignoreSettings = true,
- bool addImportDirs = true);
+ bool addImportDirs = true, bool absolutePath = true);
void callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings = nullptr,
QStringList importDirs = {}, QStringList qmltypesFiles = {},
QStringList resources = {},
DefaultImportOption defaultImports = UseDefaultImports,
QMap<QString, QQmlJSLogger::Option> *options = nullptr);
- void searchWarnings(const QJsonArray &warnings, const QString &string, const QString &filename,
+ void searchWarnings(const QJsonArray &warnings, const QString &string,
+ QtMsgType type = QtWarningMsg, quint32 line = 0, quint32 column = 0,
ContainOption shouldContain = StringContained,
ReplacementOption searchReplacements = NoReplacementSearch);
+ template<typename ExpectedMessageFailureHandler, typename BadMessageFailureHandler>
+ void checkResult(const QJsonArray &warnings, const Result &result,
+ ExpectedMessageFailureHandler onExpectedMessageFailures,
+ BadMessageFailureHandler onBadMessageFailures);
+
+ void checkResult(const QJsonArray &warnings, const Result &result)
+ {
+ checkResult(
+ warnings, result, [] {}, [] {});
+ }
+
+ void runTest(const QString &testFile, const Result &result, QStringList importDirs = {},
+ QStringList qmltypesFiles = {}, QStringList resources = {},
+ DefaultImportOption defaultImports = UseDefaultImports,
+ QMap<QString, QQmlJSLogger::Option> *options = nullptr);
+
QString m_qmllintPath;
QString m_qmljsrootgenPath;
QString m_qmltyperegistrarPath;
@@ -118,6 +157,8 @@ private:
QQmlJSLinter m_linter;
};
+Q_DECLARE_METATYPE(TestQmllint::Result)
+
TestQmllint::TestQmllint()
: QQmlDataTest(QT_QMLTEST_DATADIR),
m_defaultImportPaths({ QLibraryInfo::path(QLibraryInfo::QmlImportsPath), dataDirectory() }),
@@ -184,14 +225,14 @@ void TestQmllint::testUnqualified_data()
// access injected name from signal
QTest::newRow("SignalHandler1")
<< QStringLiteral("SignalHandler.qml")
- << QStringLiteral("onDoubleClicked: function(mouse) {") << 5 << 21;
+ << QStringLiteral("onDoubleClicked: function(mouse) {") << 5 << 21;
QTest::newRow("SignalHandler2")
<< QStringLiteral("SignalHandler.qml")
- << QStringLiteral("onPositionChanged: function(mouse) {") << 10 << 21;
+ << QStringLiteral("onPositionChanged: function(mouse) {") << 10 << 21;
QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml")
- << QStringLiteral("onClicked: (mouse) => ") << 8 << 29;
+ << QStringLiteral("onClicked: (mouse) => ") << 8 << 29;
QTest::newRow("SignalHandlerShort2")
- << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => ")
+ << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => ")
<< 12 << 34;
// access catch identifier outside catch block
QTest::newRow("CatchStatement") << QStringLiteral("CatchStatement.qml") << QStringLiteral("err") << 6 << 21;
@@ -205,56 +246,38 @@ void TestQmllint::testUnqualified_data()
void TestQmllint::testUnknownCausesFail()
{
- {
- QJsonArray warnings;
- callQmllint("unknownElement.qml", false, &warnings);
- searchWarnings(
- warnings,
- QStringLiteral(
- "Warning: %1:4:5: Unknown was not found. Did you add all import paths?")
- .arg(testFile("unknownElement.qml")),
- "unknownElement.qml");
- }
- {
- QJsonArray warnings;
- callQmllint("TypeWithUnknownPropertyType.qml", false, &warnings);
- searchWarnings(
- warnings,
- QStringLiteral(
- "Warning: %1:4:5: Something was not found. Did you add all import paths?")
- .arg(testFile("TypeWithUnknownPropertyType.qml")),
- "TypeWithUnknownPropertyType.qml");
- }
+ runTest("unknownElement.qml",
+ Result { { Message {
+ QStringLiteral("Unknown was not found. Did you add all import paths?"), 4, 5,
+ QtWarningMsg } } });
+ runTest("TypeWithUnknownPropertyType.qml",
+ Result { { Message {
+ QStringLiteral("Something was not found. Did you add all import paths?"), 4, 5,
+ QtWarningMsg } } });
}
void TestQmllint::directoryPassedAsQmlTypesFile()
{
- QJsonArray warnings;
- callQmllint("unknownElement.qml", false, &warnings, {}, { dataDirectory() });
- searchWarnings(warnings,
- QStringLiteral("QML types file cannot be a directory: ") + dataDirectory(),
- "unknownElement.qml");
+ runTest("unknownElement.qml",
+ Result { { Message { QStringLiteral("QML types file cannot be a directory: ")
+ + dataDirectory() } } },
+ {}, { dataDirectory() });
}
void TestQmllint::oldQmltypes()
{
- QJsonArray warnings;
- callQmllint("oldQmltypes.qml", false, &warnings);
- searchWarnings(warnings, QStringLiteral("typeinfo not declared in qmldir file"),
- "oldQmltypes.qml");
- searchWarnings(warnings,
- QStringLiteral("QQuickItem was not found. Did you add all import paths?"),
- "oldQmltypes.qml", StringNotContained);
- searchWarnings(warnings, QStringLiteral("Found deprecated dependency specifications"),
- "oldQmltypes.qml");
-
- // Checking for both lines separately so that we don't have to mess with the line endings.b
- searchWarnings(warnings,
- QStringLiteral("Meta object revision and export version differ."),
- "oldQmltypes.qml");
- searchWarnings(warnings,
- QStringLiteral("Revision 0 corresponds to version 0.0; it should be 1.0."),
- "oldQmltypes.qml");
+ runTest("oldQmltypes.qml",
+ Result { {
+ Message { QStringLiteral("typeinfo not declared in qmldir file") },
+ Message {
+ QStringLiteral("Found deprecated dependency specifications") },
+ Message { QStringLiteral(
+ "Meta object revision and export version differ.") },
+ Message { QStringLiteral(
+ "Revision 0 corresponds to version 0.0; it should be 1.0.") },
+ },
+ { Message { QStringLiteral(
+ "QQuickItem was not found. Did you add all import paths?") } } });
}
void TestQmllint::qmltypes_data()
@@ -366,509 +389,595 @@ void TestQmllint::resources()
void TestQmllint::dirtyQmlCode_data()
{
QTest::addColumn<QString>("filename");
- QTest::addColumn<QString>("warningMessage");
- QTest::addColumn<QString>("notContained");
- QTest::addColumn<QString>("replacement");
- QTest::addColumn<bool>("exitsNormally");
+ QTest::addColumn<Result>("result");
QTest::newRow("Invalid_syntax_QML")
- << QStringLiteral("failure1.qml") << QStringLiteral("%1:4:8: Expected token `:'")
- << QString() << QString() << false;
- QTest::newRow("Invalid_syntax_JS")
- << QStringLiteral("failure1.js") << QStringLiteral("%1:4:12: Expected token `;'")
- << QString() << QString() << false;
+ << QStringLiteral("failure1.qml")
+ << Result { { Message { QStringLiteral("Expected token `:'"), 4, 8, QtCriticalMsg } } };
+ QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js")
+ << Result { { Message { QStringLiteral("Expected token `;'"),
+ 4, 12, QtCriticalMsg } } };
QTest::newRow("AutomatchedSignalHandler")
<< QStringLiteral("AutomatchedSignalHandler.qml")
- << QString("Warning: %1:12:36: Unqualified access") << QString() << QString() << false;
+ << Result { { Message { QStringLiteral("Unqualified access"), 12, 36 } } };
QTest::newRow("AutomatchedSignalHandler2")
<< QStringLiteral("AutomatchedSignalHandler.qml")
- << QString("Info: Implicitly defining onClicked as signal handler") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Implicitly defining onClicked as signal handler"), 0, 0,
+ QtInfoMsg } } };
QTest::newRow("MemberNotFound")
<< QStringLiteral("memberNotFound.qml")
- << QString("Warning: %1:6:31: Property \"foo\" not found on type \"QtObject\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"foo\" not found on type \"QtObject\""), 6,
+ 31 } } };
QTest::newRow("UnknownJavascriptMethd")
<< QStringLiteral("unknownJavascriptMethod.qml")
- << QString("Warning: %1:5:25: Property \"foo2\" not found on type \"Methods\"")
- << QString() << QString() << false;
- QTest::newRow("badAlias") << QStringLiteral("badAlias.qml")
- << QString("Warning: %1:3:1: Cannot resolve alias \"wrong\"")
- << QString() << QString() << false;
- QTest::newRow("badAliasProperty1") << QStringLiteral("badAliasProperty.qml")
- << QString("Warning: %1:3:1: Cannot resolve alias \"wrong\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"foo2\" not found on type \"Methods\""), 5,
+ 25 } } };
+ QTest::newRow("badAlias")
+ << QStringLiteral("badAlias.qml")
+ << Result { { Message { QStringLiteral("Cannot resolve alias \"wrong\""), 3, 1 } } };
+ QTest::newRow("badAliasProperty1")
+ << QStringLiteral("badAliasProperty.qml")
+ << Result { { Message { QStringLiteral("Cannot resolve alias \"wrong\""), 3, 1 } } };
QTest::newRow("badAliasExpression")
<< QStringLiteral("badAliasExpression.qml")
- << QString("Warning: %1:5:26: Invalid alias expression. Only IDs and field member "
- "expressions can be aliased")
- << QString() << QString() << false;
- QTest::newRow("aliasCycle1") << QStringLiteral(
- "aliasCycle.qml") << QString("Warning: %1:3:1: Alias \"b\" is part of an alias cycle")
- << QString() << QString() << false;
- QTest::newRow("aliasCycle2") << QStringLiteral(
- "aliasCycle.qml") << QString("Warning: %1:3:1: Alias \"a\" is part of an alias cycle")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Invalid alias expression. Only IDs and field member "
+ "expressions can be aliased"),
+ 5, 26 } } };
+ QTest::newRow("aliasCycle1") << QStringLiteral("aliasCycle.qml")
+ << Result { { Message {
+ QStringLiteral("Alias \"b\" is part of an alias cycle"),
+ 3, 1 } } };
+ QTest::newRow("aliasCycle2") << QStringLiteral("aliasCycle.qml")
+ << Result { { Message {
+ QStringLiteral("Alias \"a\" is part of an alias cycle"),
+ 3, 1 } } };
QTest::newRow("badParent")
<< QStringLiteral("badParent.qml")
- << QString("Warning: %1:5:34: Property \"rrr\" not found on type \"Item\"") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral("Property \"rrr\" not found on type \"Item\""),
+ 5, 34 } } };
QTest::newRow("parentIsComponent")
<< QStringLiteral("parentIsComponent.qml")
- << QString("Warning: %1:7:39: Property \"progress\" not found on type \"QQuickItem\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"progress\" not found on type \"QQuickItem\""), 7,
+ 39 } } };
QTest::newRow("badTypeAssertion")
<< QStringLiteral("badTypeAssertion.qml")
- << QString("Warning: %1:5:39: Property \"rrr\" not found on type \"QQuickItem\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"rrr\" not found on type \"QQuickItem\""), 5,
+ 39 } } };
QTest::newRow("incompleteQmltypes")
<< QStringLiteral("incompleteQmltypes.qml")
- << QString("Warning: %1:5:26: Type \"QPalette\" of property \"palette\" not found")
- << QString() << QString() << false;
- QTest::newRow("incompleteQmltypes2") << QStringLiteral("incompleteQmltypes2.qml")
- << QString("Warning: %1:5:35: Property \"weDontKnowIt\" "
- "not found on type \"CustomPalette\"")
- << QString() << QString() << false;
- QTest::newRow("inheritanceCylce") << QStringLiteral("Cycle1.qml")
- << QString("Warning: %1: Cycle2 is part of an inheritance "
- "cycle: Cycle2 -> Cycle3 -> Cycle1 -> Cycle2")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Type \"QPalette\" of property \"palette\" not found"), 5,
+ 26 } } };
+ QTest::newRow("incompleteQmltypes2")
+ << QStringLiteral("incompleteQmltypes2.qml")
+ << Result { { Message { QStringLiteral("Property \"weDontKnowIt\" "
+ "not found on type \"CustomPalette\""),
+ 5, 35 } } };
+ QTest::newRow("incompleteQmltypes3")
+ << QStringLiteral("incompleteQmltypes3.qml")
+ << Result { { Message {
+ QStringLiteral("Type \"QPalette\" of property \"palette\" not found"), 5,
+ 21 } } };
+ QTest::newRow("inheritanceCycle")
+ << QStringLiteral("Cycle1.qml")
+ << Result { { Message {
+ QStringLiteral("Cycle2 is part of an inheritance cycle: Cycle2 -> Cycle3 "
+ "-> Cycle1 -> Cycle2"),
+ 2, 1 } } };
QTest::newRow("badQmldirImportAndDepend")
<< QStringLiteral("qmldirImportAndDepend/bad.qml")
- << QString("Warning: %1:3:1: Item was not found. Did you add all import paths?")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Item was not found. Did you add all import paths?"), 3,
+ 1 } } };
QTest::newRow("javascriptMethodsInModule")
<< QStringLiteral("javascriptMethodsInModuleBad.qml")
- << QString("Warning: %1:5:21: Property \"unknownFunc\" not found on type \"Foo\"")
- << QString() << QString() << false;
- QTest::newRow("badEnumFromQtQml") << QStringLiteral("badEnumFromQtQml.qml")
- << QString("Warning: %1:4:30: Property \"Linear123\" not "
- "found on type \"QQmlEasingEnums\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"unknownFunc\" not found on type \"Foo\""), 5,
+ 21 } } };
+ QTest::newRow("badEnumFromQtQml")
+ << QStringLiteral("badEnumFromQtQml.qml")
+ << Result { { Message { QStringLiteral("Property \"Linear123\" not "
+ "found on type \"QQmlEasingEnums\""),
+ 4, 30 } } };
QTest::newRow("anchors3")
<< QStringLiteral("anchors3.qml")
- << QString("Cannot assign binding of type QQuickItem to QQuickAnchorLine") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign binding of type QQuickItem to QQuickAnchorLine") } } };
QTest::newRow("nanchors1") << QStringLiteral("nanchors1.qml")
- << QString("unknown grouped property scope nanchors.") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "unknown grouped property scope nanchors.") } } };
QTest::newRow("nanchors2") << QStringLiteral("nanchors2.qml")
- << QString("unknown grouped property scope nanchors.") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "unknown grouped property scope nanchors.") } } };
QTest::newRow("nanchors3") << QStringLiteral("nanchors3.qml")
- << QString("unknown grouped property scope nanchors.") << QString()
- << QString() << false;
- QTest::newRow("badAliasObject") << QStringLiteral("badAliasObject.qml")
- << QString("Warning: %1:8:40: Property \"wrongwrongwrong\" not "
- "found on type \"QtObject\"")
- << QString() << QString() << false;
- QTest::newRow("badScript")
- << QStringLiteral("badScript.qml")
- << QString("Warning: %1:5:21: Property \"stuff\" not found on type \"Empty\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "unknown grouped property scope nanchors.") } } };
+ QTest::newRow("badAliasObject")
+ << QStringLiteral("badAliasObject.qml")
+ << Result { { Message { QStringLiteral("Property \"wrongwrongwrong\" not "
+ "found on type \"QtObject\""),
+ 8, 40 } } };
+ QTest::newRow("badScript") << QStringLiteral("badScript.qml")
+ << Result { { Message {
+ QStringLiteral(
+ "Property \"stuff\" not found on type \"Empty\""),
+ 5, 21 } } };
QTest::newRow("badScriptOnAttachedProperty")
<< QStringLiteral("badScript.attached.qml")
- << QString("Warning: %1:3:26: Unqualified access") << QString() << QString() << false;
- QTest::newRow("brokenNamespace") << QStringLiteral("brokenNamespace.qml")
- << QString("Warning: %1:4:19: Type not found in namespace")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral("Unqualified access"), 3, 26 } } };
+ QTest::newRow("brokenNamespace")
+ << QStringLiteral("brokenNamespace.qml")
+ << Result { { Message { QStringLiteral("Type not found in namespace"), 4, 19 } } };
QTest::newRow("segFault (bad)")
<< QStringLiteral("SegFault.bad.qml")
- << QStringLiteral("Property \"foobar\" not found on type \"QQuickScreenAttached\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"foobar\" not found on type \"QQuickScreenAttached\"") } } };
QTest::newRow("VariableUsedBeforeDeclaration")
<< QStringLiteral("useBeforeDeclaration.qml")
- << QStringLiteral("%1:5:9: Variable \"argq\" is used here before its declaration. "
- "The declaration is at 6:13.")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Variable \"argq\" is used here before its declaration. "
+ "The declaration is at 6:13."),
+ 5, 9 } } };
QTest::newRow("SignalParameterMismatch")
<< QStringLiteral("namedSignalParameters.qml")
- << QStringLiteral("Parameter 1 to signal handler for \"onSig\" is called \"argarg\". "
- "The signal has a parameter of the same name in position 2.")
- << QStringLiteral("onSig2") << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Parameter 1 to signal handler for \"onSig\" is called \"argarg\". "
+ "The signal has a parameter of the same name in position 2.") } },
+ { Message { QStringLiteral("onSig2") } } };
QTest::newRow("TooManySignalParameters")
<< QStringLiteral("tooManySignalParameters.qml")
- << QStringLiteral("Signal handler for \"onSig\" has more formal parameters "
- "than the signal it handles.")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Signal handler for \"onSig\" has more formal parameters "
+ "than the signal it handles.") } } };
QTest::newRow("OnAssignment") << QStringLiteral("onAssignment.qml")
- << QStringLiteral("Property \"loops\" not found on type \"bool\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"loops\" not found on type \"bool\"") } } };
QTest::newRow("BadAttached") << QStringLiteral("badAttached.qml")
- << QStringLiteral("unknown attached property scope WrongAttached.")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "unknown attached property scope WrongAttached.") } } };
QTest::newRow("BadBinding") << QStringLiteral("badBinding.qml")
- << QStringLiteral(
+ << Result { { Message { QStringLiteral(
"Binding assigned to \"doesNotExist\", but no property "
- "\"doesNotExist\" exists in the current element.")
- << QString() << QString() << false;
+ "\"doesNotExist\" exists in the current element.") } } };
QTest::newRow("bad template literal (simple)")
<< QStringLiteral("badTemplateStringSimple.qml")
- << QStringLiteral("Cannot assign binding of type string to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type string to int") } } };
QTest::newRow("bad template literal (substitution)")
<< QStringLiteral("badTemplateStringSubstitution.qml")
- << QStringLiteral("Cannot assign binding of type QString to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type QString to int") } } };
QTest::newRow("bad constant number to string")
<< QStringLiteral("numberToStringProperty.qml")
- << QStringLiteral("Cannot assign a numeric constant to a string property") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign a numeric constant to a string property") } } };
QTest::newRow("bad unary minus to string")
<< QStringLiteral("unaryMinusToStringProperty.qml")
- << QStringLiteral("Cannot assign a numeric constant to a string property") << QString()
- << QString() << false;
- QTest::newRow("bad tranlsation binding (qsTr)")
- << QStringLiteral("bad_qsTr.qml") << QStringLiteral("") << QString() << QString()
- << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign a numeric constant to a string property") } } };
+ QTest::newRow("bad tranlsation binding (qsTr)") << QStringLiteral("bad_qsTr.qml") << Result {};
QTest::newRow("bad string binding (QT_TR_NOOP)")
<< QStringLiteral("bad_QT_TR_NOOP.qml")
- << QStringLiteral("Cannot assign binding of type string to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type string to int") } } };
QTest::newRow("BadScriptBindingOnGroup")
<< QStringLiteral("badScriptBinding.group.qml")
- << QStringLiteral("Warning: %1:3:10: Binding assigned to \"bogusProperty\", but no "
- "property \"bogusProperty\" exists in the current element.")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Binding assigned to \"bogusProperty\", but no "
+ "property \"bogusProperty\" exists in the current element."),
+ 3, 10 } } };
QTest::newRow("BadScriptBindingOnAttachedType")
<< QStringLiteral("badScriptBinding.attached.qml")
- << QStringLiteral("Warning: %1:5:12: Binding assigned to \"bogusProperty\", but no "
- "property \"bogusProperty\" exists in the current element.")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Binding assigned to \"bogusProperty\", but no "
+ "property \"bogusProperty\" exists in the current element."),
+ 5, 12 } } };
QTest::newRow("BadScriptBindingOnAttachedSignalHandler")
<< QStringLiteral("badScriptBinding.attachedSignalHandler.qml")
- << QStringLiteral(
- "Warning: %1:3:10: no matching signal found for handler \"onBogusSignal\"")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("no matching signal found for handler \"onBogusSignal\""), 3,
+ 10 } } };
QTest::newRow("BadPropertyType")
<< QStringLiteral("badPropertyType.qml")
- << QStringLiteral("No type found for property \"bad\". This may be due to a missing "
- "import statement or incomplete qmltypes files.")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "No type found for property \"bad\". This may be due to a missing "
+ "import statement or incomplete qmltypes files.") } } };
QTest::newRow("Deprecation (Property, with reason)")
<< QStringLiteral("deprecatedPropertyReason.qml")
- << QStringLiteral("Property \"deprecated\" is deprecated (Reason: Test)") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"deprecated\" is deprecated (Reason: Test)") } } };
QTest::newRow("Deprecation (Property, no reason)")
<< QStringLiteral("deprecatedProperty.qml")
- << QStringLiteral("Property \"deprecated\" is deprecated") << QString() << QString()
- << false;
+ << Result { { Message { QStringLiteral("Property \"deprecated\" is deprecated") } } };
QTest::newRow("Deprecation (Property binding, with reason)")
<< QStringLiteral("deprecatedPropertyBindingReason.qml")
- << QStringLiteral("Binding on deprecated property \"deprecatedReason\" (Reason: Test)")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Binding on deprecated property \"deprecatedReason\" (Reason: Test)") } } };
QTest::newRow("Deprecation (Property binding, no reason)")
<< QStringLiteral("deprecatedPropertyBinding.qml")
- << QStringLiteral("Binding on deprecated property \"deprecated\"") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Binding on deprecated property \"deprecated\"") } } };
QTest::newRow("Deprecation (Type, with reason)")
<< QStringLiteral("deprecatedTypeReason.qml")
- << QStringLiteral("Type \"TypeDeprecatedReason\" is deprecated (Reason: Test)")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Type \"TypeDeprecatedReason\" is deprecated (Reason: Test)") } } };
QTest::newRow("Deprecation (Type, no reason)")
<< QStringLiteral("deprecatedType.qml")
- << QStringLiteral("Type \"TypeDeprecated\" is deprecated") << QString() << QString()
- << false;
+ << Result { { Message { QStringLiteral("Type \"TypeDeprecated\" is deprecated") } } };
QTest::newRow("MissingDefaultProperty")
<< QStringLiteral("defaultPropertyWithoutKeyword.qml")
- << QStringLiteral("Cannot assign to non-existent default property") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign to non-existent default property") } } };
QTest::newRow("MissingDefaultPropertyDefinedInTheSameType")
<< QStringLiteral("defaultPropertyWithinTheSameType.qml")
- << QStringLiteral("Cannot assign to non-existent default property") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign to non-existent default property") } } };
QTest::newRow("DoubleAssignToDefaultProperty")
<< QStringLiteral("defaultPropertyWithDoubleAssignment.qml")
- << QStringLiteral("Cannot assign multiple objects to a default non-list property")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign multiple objects to a default non-list property") } } };
QTest::newRow("DefaultPropertyWithWrongType(string)")
<< QStringLiteral("defaultPropertyWithWrongType.qml")
- << QStringLiteral("Cannot assign to default property of incompatible type")
- << QStringLiteral("Cannot assign to non-existent default property") << QString()
- << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign to default property of incompatible type") } },
+ { Message { QStringLiteral(
+ "Cannot assign to non-existent default property") } } };
QTest::newRow("MultiDefaultPropertyWithWrongType")
<< QStringLiteral("multiDefaultPropertyWithWrongType.qml")
- << QStringLiteral("Cannot assign to default property of incompatible type")
- << QStringLiteral("Cannot assign to non-existent default property") << QString()
- << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign to default property of incompatible type") } },
+ { Message { QStringLiteral(
+ "Cannot assign to non-existent default property") } } };
QTest::newRow("InvalidImport")
<< QStringLiteral("invalidImport.qml")
- << QStringLiteral("Failed to import FooBar. Are your import paths set up properly?")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Failed to import FooBar. Are your import paths set up properly?") } } };
QTest::newRow("Unused Import (simple)")
- << QStringLiteral("unused_simple.qml") << QStringLiteral("Unused import at %1:1:1")
- << QString() << QString() << true;
+ << QStringLiteral("unused_simple.qml")
+ << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } },
+ {},
+ {},
+ Result::ExitsNormally };
QTest::newRow("Unused Import (prefix)")
- << QStringLiteral("unused_prefix.qml") << QStringLiteral("Unused import at %1:1:1")
- << QString() << QString() << true;
- QTest::newRow("TypePropertAccess") << QStringLiteral("typePropertyAccess.qml") << QString()
- << QString() << QString() << false;
+ << QStringLiteral("unused_prefix.qml")
+ << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } },
+ {},
+ {},
+ Result::ExitsNormally };
+ QTest::newRow("TypePropertAccess") << QStringLiteral("typePropertyAccess.qml") << Result {};
QTest::newRow("badAttachedProperty")
<< QStringLiteral("badAttachedProperty.qml")
- << QString("Property \"progress\" not found on type \"TestType\"") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"progress\" not found on type \"TestType\"") } } };
QTest::newRow("badAttachedPropertyNested")
<< QStringLiteral("badAttachedPropertyNested.qml")
- << QString("12:41: Property \"progress\" not found on type \"QObject\"")
- << QString("6:37: Property \"progress\" not found on type \"QObject\"") << QString()
- << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"progress\" not found on type \"QObject\""),
+ 12, 41 } },
+ { Message { QString("Property \"progress\" not found on type \"QObject\""),
+ 6, 37 } } };
QTest::newRow("badAttachedPropertyTypeString")
<< QStringLiteral("badAttachedPropertyTypeString.qml")
- << QString("Cannot assign binding of type string to int") << QString() << QString()
- << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type string to int") } } };
QTest::newRow("badAttachedPropertyTypeQtObject")
<< QStringLiteral("badAttachedPropertyTypeQtObject.qml")
- << QString("Property \"count\" of type \"int\" is assigned an incompatible type "
- "\"QtObject\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"count\" of type \"int\" is assigned an incompatible type "
+ "\"QtObject\"") } } };
// should succeed, but it does not:
- QTest::newRow("attachedPropertyAccess") << QStringLiteral("goodAttachedPropertyAccess.qml")
- << QString() << QString() << QString() << true;
+ QTest::newRow("attachedPropertyAccess")
+ << QStringLiteral("goodAttachedPropertyAccess.qml") << Result::clean();
// should succeed, but it does not:
- QTest::newRow("attachedPropertyNested") << QStringLiteral("goodAttachedPropertyNested.qml")
- << QString() << QString() << QString() << true;
+ QTest::newRow("attachedPropertyNested")
+ << QStringLiteral("goodAttachedPropertyNested.qml") << Result::clean();
QTest::newRow("deprecatedFunction")
<< QStringLiteral("deprecatedFunction.qml")
- << QStringLiteral("Method \"deprecated(foobar)\" is deprecated (Reason: No particular "
- "reason.)")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Method \"deprecated(foobar)\" is deprecated (Reason: No particular "
+ "reason.)") } } };
QTest::newRow("deprecatedFunctionInherited")
<< QStringLiteral("deprecatedFunctionInherited.qml")
- << QStringLiteral("Method \"deprecatedInherited(c, d)\" is deprecated (Reason: This "
- "deprecation should be visible!)")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Method \"deprecatedInherited(c, d)\" is deprecated (Reason: This "
+ "deprecation should be visible!)") } } };
QTest::newRow("duplicated id")
- << QStringLiteral("duplicateId.qml")
- << QStringLiteral("Found a duplicated id. id root was first declared ")
- << QString() << QString() << false;
+ << QStringLiteral("duplicateId.qml")
+ << Result { { Message {
+ QStringLiteral("Found a duplicated id. id root was first declared ") } } };
QTest::newRow("string as id") << QStringLiteral("stringAsId.qml")
- << QStringLiteral("ids do not need quotation marks") << QString()
- << QString() << false;
- QTest::newRow("stringIdUsedInWarning") << QStringLiteral("stringIdUsedInWarning.qml")
- << QStringLiteral("i is a member of a parent element")
- << QString() << QStringLiteral("stringy.") << false;
+ << Result { { Message { QStringLiteral(
+ "ids do not need quotation marks") } } };
+ QTest::newRow("stringIdUsedInWarning")
+ << QStringLiteral("stringIdUsedInWarning.qml")
+ << Result { { Message {
+ QStringLiteral("i is a member of a parent element"),
+ } },
+ {},
+ { Message { QStringLiteral("stringy.") } } };
QTest::newRow("Invalid id (expression)")
- << QStringLiteral("invalidId1.qml") << QStringLiteral("Failed to parse id") << QString()
- << QString() << false;
+ << QStringLiteral("invalidId1.qml")
+ << Result { { Message { QStringLiteral("Failed to parse id") } } };
QTest::newRow("multilineString")
<< QStringLiteral("multilineString.qml")
- << QStringLiteral("String contains unescaped line terminator which is deprecated. Use "
- "a template literal instead.")
- << QString() << QString() << true;
+ << Result { { Message { QStringLiteral("String contains unescaped line terminator "
+ "which is deprecated."),
+ 0, 0, QtInfoMsg } },
+ {},
+ {},
+ Result::ExitsNormally };
+ QTest::newRow("multilineStringTortureQuote")
+ << QStringLiteral("multilineStringTortureQuote.qml")
+ << Result { { Message { QStringLiteral("String contains unescaped line terminator which is deprecated."), 0, 0, QtInfoMsg }}, {}, {Message {R"(`
+ quote: " \\" \\\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: ' \' \\' \\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ `)"}}, Result::ExitsNormally };
+ QTest::newRow("multilineStringTortureTick")
+ << QStringLiteral("multilineStringTortureTick.qml")
+ << Result { { Message { QStringLiteral("String contains unescaped line terminator which is deprecated."), 0, 0, QtInfoMsg } }, {}, {Message {
+ R"(`
+ quote: " \" \\" \\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: ' \\' \\\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ `)"
+}}, Result::ExitsNormally};
QTest::newRow("unresolvedType")
<< QStringLiteral("unresolvedType.qml")
- << QStringLiteral("UnresolvedType was not found. Did you add all import paths?")
- << QStringLiteral("incompatible type") << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "UnresolvedType was not found. Did you add all import paths?") } },
+ { Message { QStringLiteral("incompatible type") } } };
QTest::newRow("invalidInterceptor")
<< QStringLiteral("invalidInterceptor.qml")
- << QStringLiteral("On-binding for property \"angle\" has wrong type \"Item\"")
- << QString() << QString() << false;
- QTest::newRow("2Interceptors") << QStringLiteral("2interceptors.qml")
- << QStringLiteral("Duplicate interceptor on property \"x\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "On-binding for property \"angle\" has wrong type \"Item\"") } } };
+ QTest::newRow("2Interceptors")
+ << QStringLiteral("2interceptors.qml")
+ << Result { { Message { QStringLiteral("Duplicate interceptor on property \"x\"") } } };
QTest::newRow("ValueSource+2Interceptors")
<< QStringLiteral("valueSourceBetween2interceptors.qml")
- << QStringLiteral("Duplicate interceptor on property \"x\"") << QString() << QString()
- << false;
+ << Result { { Message { QStringLiteral("Duplicate interceptor on property \"x\"") } } };
QTest::newRow("2ValueSources") << QStringLiteral("2valueSources.qml")
- << QStringLiteral("Duplicate value source on property \"x\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Duplicate value source on property \"x\"") } } };
QTest::newRow("ValueSource+Value")
<< QStringLiteral("valueSource_Value.qml")
- << QStringLiteral("Cannot combine value source and binding on property \"obj\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot combine value source and binding on property \"obj\"") } } };
QTest::newRow("ValueSource+ListValue")
<< QStringLiteral("valueSource_listValue.qml")
- << QStringLiteral("Cannot combine value source and binding on property \"objs\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot combine value source and binding on property \"objs\"") } } };
QTest::newRow("NonExistentListProperty")
<< QStringLiteral("nonExistentListProperty.qml")
- << QStringLiteral("Property \"objs\" is invalid or does not exist") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"objs\" is invalid or does not exist") } } };
QTest::newRow("QtQuick.Window 2.0")
<< QStringLiteral("qtquickWindow20.qml")
- << QStringLiteral("Property \"window\" not found on type \"QQuickWindow\"") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"window\" not found on type \"QQuickWindow\"") } } };
QTest::newRow("unresolvedAttachedType")
<< QStringLiteral("unresolvedAttachedType.qml")
- << QStringLiteral("unknown attached property scope UnresolvedAttachedType.")
- << QStringLiteral("Property \"property\" is invalid or does not exist") << QString()
- << false;
+ << Result { { Message { QStringLiteral(
+ "unknown attached property scope UnresolvedAttachedType.") } },
+ { Message { QStringLiteral(
+ "Property \"property\" is invalid or does not exist") } } };
QTest::newRow("nestedInlineComponents")
<< QStringLiteral("nestedInlineComponents.qml")
- << QStringLiteral("Nested inline components are not supported") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Nested inline components are not supported") } } };
QTest::newRow("WithStatement") << QStringLiteral("WithStatement.qml")
- << QStringLiteral("with statements are strongly discouraged")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "with statements are strongly discouraged") } } };
QTest::newRow("BindingTypeMismatch")
<< QStringLiteral("bindingTypeMismatch.qml")
- << QStringLiteral("Cannot assign binding of type QString to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type QString to int") } } };
QTest::newRow("BindingTypeMismatchFunction")
<< QStringLiteral("bindingTypeMismatchFunction.qml")
- << QStringLiteral("Cannot assign binding of type QString to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type QString to int") } } };
QTest::newRow("BadLiteralBinding")
<< QStringLiteral("badLiteralBinding.qml")
- << QStringLiteral("Cannot assign binding of type string to int") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type string to int") } } };
QTest::newRow("BadLiteralBindingDate")
<< QStringLiteral("badLiteralBindingDate.qml")
- << QStringLiteral("Cannot assign binding of type QString to QDateTime") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign binding of type QString to QDateTime") } } };
QTest::newRow("BadModulePrefix")
<< QStringLiteral("badModulePrefix.qml")
- << QStringLiteral("Cannot load singleton as property of object") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot load singleton as property of object") } } };
QTest::newRow("BadModulePrefix2")
<< QStringLiteral("badModulePrefix2.qml")
- << QStringLiteral("Cannot use non-reference type QRectF as base "
- "of namespaced attached type")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral("Cannot use non-reference type QRectF as base "
+ "of namespaced attached type") } } };
QTest::newRow("AssignToReadOnlyProperty")
<< QStringLiteral("assignToReadOnlyProperty.qml")
- << QStringLiteral("Cannot assign to read-only property activeFocus") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign to read-only property activeFocus") } } };
QTest::newRow("AssignToReadOnlyProperty")
<< QStringLiteral("assignToReadOnlyProperty2.qml")
- << QStringLiteral("Cannot assign to read-only property activeFocus") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign to read-only property activeFocus") } } };
QTest::newRow("DeferredPropertyID")
<< QStringLiteral("deferredPropertyID.qml")
- << QStringLiteral(
+ << Result { { Message { QStringLiteral(
"Cannot defer property assignment to "
"\"contentData\". Assigning an id to an object or one of its sub-objects "
- "bound to a deferred property will make the assignment immediate.")
- << QString() << QString() << false;
+ "bound to a deferred property will make the assignment immediate.") } } };
QTest::newRow("DeferredPropertyNestedID")
<< QStringLiteral("deferredPropertyNestedID.qml")
- << QStringLiteral(
+ << Result { { Message { QStringLiteral(
"Cannot defer property assignment to "
"\"contentData\". Assigning an id to an object or one of its sub-objects "
- "bound to a deferred property will make the assignment immediate.")
- << QString() << QString() << false;
+ "bound to a deferred property will make the assignment immediate.") } } };
QTest::newRow("cachedDependency")
- << QStringLiteral("cachedDependency.qml") << QStringLiteral("Unused import at %1:1:1")
- << QStringLiteral("Cannot assign binding of type QQuickItem to QObject") << QString()
- << true;
+ << QStringLiteral("cachedDependency.qml")
+ << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } },
+ { Message { QStringLiteral(
+ "Cannot assign binding of type QQuickItem to QObject") } },
+ {},
+ Result::ExitsNormally };
QTest::newRow("cycle in import")
<< QStringLiteral("cycleHead.qml")
- << QStringLiteral("MenuItem is part of an inheritance cycle: MenuItem -> MenuItem")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "MenuItem is part of an inheritance cycle: MenuItem -> MenuItem") } } };
QTest::newRow("badGeneralizedGroup1")
<< QStringLiteral("badGeneralizedGroup1.qml")
- << QStringLiteral("Binding assigned to \"aaaa\", "
- "but no property \"aaaa\" exists in the current element")
- << QString() << QString() << false;
- QTest::newRow("badGeneralizedGroup2") << QStringLiteral("badGeneralizedGroup2.qml")
- << QStringLiteral("unknown grouped property scope aself")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Binding assigned to \"aaaa\", "
+ "but no property \"aaaa\" exists in the current element") } } };
+ QTest::newRow("badGeneralizedGroup2")
+ << QStringLiteral("badGeneralizedGroup2.qml")
+ << Result { { Message { QStringLiteral("unknown grouped property scope aself") } } };
QTest::newRow("missingQmltypes")
<< QStringLiteral("missingQmltypes.qml")
- << QStringLiteral("QML types file does not exist") << QString() << QString() << false;
- QTest::newRow("enumInvalid") << QStringLiteral(
- "enumInvalid.qml") << QStringLiteral("Property \"red\" not found on type \"QtObject\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral("QML types file does not exist") } } };
+ QTest::newRow("enumInvalid")
+ << QStringLiteral("enumInvalid.qml")
+ << Result { { Message {
+ QStringLiteral("Property \"red\" not found on type \"QtObject\"") } } };
QTest::newRow("inaccessibleId")
<< QStringLiteral("inaccessibleId.qml")
- << QStringLiteral("Property \"objectName\" not found on type \"int\"") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"objectName\" not found on type \"int\"") } } };
QTest::newRow("inaccessibleId2")
<< QStringLiteral("inaccessibleId2.qml")
- << QStringLiteral("Property \"objectName\" not found on type \"int\"") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Property \"objectName\" not found on type \"int\"") } } };
QTest::newRow("unknownTypeCustomParser")
<< QStringLiteral("unknownTypeCustomParser.qml")
- << QStringLiteral("TypeDoesNotExist was not found.") << QString() << QString() << false;
+ << Result { { Message { QStringLiteral("TypeDoesNotExist was not found.") } } };
QTest::newRow("nonNullStored")
<< QStringLiteral("nonNullStored.qml")
- << QStringLiteral("Property \"objectName\" not found on type \"Foozle\"")
- << QStringLiteral("Unqualified access")
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"objectName\" not found on type \"Foozle\"") } },
+ { Message { QStringLiteral("Unqualified access") } } };
QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-bindable")
<< QStringLiteral("badCppPropertyChangeHandlers1.qml")
- << QStringLiteral("Signal handler for \"onAChanged\" has more formal parameters than "
- "the signal it handles")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Signal handler for \"onAChanged\" has more formal parameters than "
+ "the signal it handles") } } };
QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-notify")
<< QStringLiteral("badCppPropertyChangeHandlers2.qml")
- << QStringLiteral("Signal handler for \"onBChanged\" has more formal parameters than "
- "the signal it handles")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Signal handler for \"onBChanged\" has more formal parameters than "
+ "the signal it handles") } } };
QTest::newRow("cppPropertyChangeHandlers-no-property")
<< QStringLiteral("badCppPropertyChangeHandlers3.qml")
- << QStringLiteral("no matching signal found for handler \"onXChanged\"") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("no matching signal found for handler \"onXChanged\"") } } };
QTest::newRow("cppPropertyChangeHandlers-not-a-signal")
<< QStringLiteral("badCppPropertyChangeHandlers4.qml")
- << QStringLiteral("no matching signal found for handler \"onWannabeSignal\"")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "no matching signal found for handler \"onWannabeSignal\"") } } };
QTest::newRow("didYouMean(binding)")
<< QStringLiteral("didYouMeanBinding.qml")
- << QStringLiteral("Binding assigned to \"witdh\", but no property \"witdh\" exists in "
- "the current element.")
- << QString() << QStringLiteral("width") << false;
+ << Result {
+ { Message { QStringLiteral(
+ "Binding assigned to \"witdh\", but no property \"witdh\" exists in "
+ "the current element.") } },
+ {},
+ { Message { QStringLiteral("width") } }
+ };
QTest::newRow("didYouMean(unqualified)")
- << QStringLiteral("didYouMeanUnqualified.qml") << QStringLiteral("Unqualified access")
- << QString() << QStringLiteral("height") << false;
+ << QStringLiteral("didYouMeanUnqualified.qml")
+ << Result { { Message { QStringLiteral("Unqualified access") } },
+ {},
+ { Message { QStringLiteral("height") } } };
QTest::newRow("didYouMean(unqualifiedCall)")
<< QStringLiteral("didYouMeanUnqualifiedCall.qml")
- << QStringLiteral("Unqualified access") << QString() << QStringLiteral("func") << false;
+ << Result { { Message { QStringLiteral("Unqualified access") } },
+ {},
+ { Message { QStringLiteral("func") } } };
QTest::newRow("didYouMean(property)")
<< QStringLiteral("didYouMeanProperty.qml")
- << QStringLiteral("Property \"hoight\" not found on type \"Rectangle\"") << QString()
- << QStringLiteral("height") << false;
+ << Result { { Message { QStringLiteral(
+ "Property \"hoight\" not found on type \"Rectangle\"") },
+ {},
+ { Message { QStringLiteral("height") } } } };
QTest::newRow("didYouMean(propertyCall)")
<< QStringLiteral("didYouMeanPropertyCall.qml")
- << QStringLiteral("Property \"lgg\" not found on type \"Console\"") << QString()
- << QStringLiteral("log") << false;
+ << Result {
+ { Message { QStringLiteral("Property \"lgg\" not found on type \"Console\"") },
+ {},
+ { Message { QStringLiteral("log") } } }
+ };
QTest::newRow("didYouMean(component)")
<< QStringLiteral("didYouMeanComponent.qml")
- << QStringLiteral("Itym was not found. Did you add all import paths?") << QString()
- << QStringLiteral("Item") << false;
+ << Result { { Message { QStringLiteral(
+ "Itym was not found. Did you add all import paths?") },
+ {},
+ { Message { QStringLiteral("Item") } } } };
QTest::newRow("nullBinding")
<< QStringLiteral("nullBinding.qml")
- << QStringLiteral("Cannot assign binding of type $internal$.std::nullptr_t to double")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign binding of type $internal$.std::nullptr_t to double") } } };
QTest::newRow("nullBindingFunction")
<< QStringLiteral("nullBindingFunction.qml")
- << QStringLiteral("Cannot assign binding of type std::nullptr_t to double") << QString()
- << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Cannot assign binding of type std::nullptr_t to double") } } };
QTest::newRow("missingRequiredAlias")
<< QStringLiteral("missingRequiredAlias.qml")
- << QStringLiteral("Component is missing required property requiredAlias from "
- "RequiredWithRootLevelAlias")
- << QString() << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Component is missing required property requiredAlias from "
+ "RequiredWithRootLevelAlias") } } };
QTest::newRow("missingSingletonPragma")
<< QStringLiteral("missingSingletonPragma.qml")
- << QStringLiteral("Type MissingPragma declared as singleton in qmldir but missing "
- "pragma Singleton")
- << QString() << QString() << false;
+ << Result { { Message { QStringLiteral(
+ "Type MissingPragma declared as singleton in qmldir but missing "
+ "pragma Singleton") } } };
QTest::newRow("missingSingletonQmldir")
<< QStringLiteral("missingSingletonQmldir.qml")
- << QStringLiteral(
+ << Result { { Message { QStringLiteral(
"Type MissingQmldirSingleton not declared as singleton in qmldir but using "
- "pragma Singleton")
- << QString() << QString() << false;
+ "pragma Singleton") } } };
QTest::newRow("jsVarDeclarationsWriteConst")
<< QStringLiteral("jsVarDeclarationsWriteConst.qml")
- << QStringLiteral("Cannot assign to read-only property constProp") << QString()
- << QString() << false;
+ << Result { { Message {
+ QStringLiteral("Cannot assign to read-only property constProp") } } };
+ QTest::newRow("shadowedSignal")
+ << QStringLiteral("shadowedSignal.qml")
+ << Result { { Message {
+ QStringLiteral("Signal \"pressed\" is shadowed by a property.") } } };
+ QTest::newRow("shadowedSignalWithId")
+ << QStringLiteral("shadowedSignalWithId.qml")
+ << Result { { Message {
+ QStringLiteral("Signal \"pressed\" is shadowed by a property") } } };
+ QTest::newRow("shadowedSlot") << QStringLiteral("shadowedSlot.qml")
+ << Result { { Message { QStringLiteral(
+ "Slot \"move\" is shadowed by a property") } } };
+ QTest::newRow("shadowedMethod") << QStringLiteral("shadowedMethod.qml")
+ << Result { { Message { QStringLiteral(
+ "Method \"foo\" is shadowed by a property.") } } };
+ QTest::newRow("callVarProp")
+ << QStringLiteral("callVarProp.qml")
+ << Result { { Message { QStringLiteral(
+ "Property \"foo\" is a variant property. It may or may not be a "
+ "method. Use a regular function instead.") } } };
+ QTest::newRow("callJSValue")
+ << QStringLiteral("callJSValueProp.qml")
+ << Result { { Message { QStringLiteral(
+ "Property \"callLater\" is a QJSValue property. It may or may not be "
+ "a method. Use a regular Q_INVOKABLE instead.") } } };
}
void TestQmllint::dirtyQmlCode()
{
QFETCH(QString, filename);
- QFETCH(QString, warningMessage);
- QFETCH(QString, notContained);
- QFETCH(QString, replacement);
- QFETCH(bool, exitsNormally);
-
- if (warningMessage.contains(QLatin1String("%1")))
- warningMessage = warningMessage.arg(testFile(filename));
+ QFETCH(Result, result);
QJsonArray warnings;
@@ -881,28 +990,21 @@ void TestQmllint::dirtyQmlCode()
QEXPECT_FAIL("bad tranlsation binding (qsTr)", "We currently do not check translation binding",
Abort);
- callQmllint(filename, exitsNormally, &warnings);
-
- if (!warningMessage.isEmpty()) {
- // output.contains() expect fails:
- QEXPECT_FAIL("BadLiteralBindingDate",
- "We're currently not able to verify any non-trivial QString conversion that "
- "requires QQmlStringConverters",
- Abort);
-
- searchWarnings(warnings, warningMessage, filename);
- }
-
- if (!notContained.isEmpty()) {
- // !output.contains() expect fails:
- QEXPECT_FAIL("badAttachedPropertyNested", "We cannot discern between types and instances",
- Abort);
-
- searchWarnings(warnings, notContained, filename, StringNotContained);
- }
+ callQmllint(filename, result.flags.testFlag(Result::ExitsNormally), &warnings);
- if (!replacement.isEmpty())
- searchWarnings(warnings, replacement, filename, StringContained, DoReplacementSearch);
+ checkResult(
+ warnings, result,
+ [] {
+ QEXPECT_FAIL("BadLiteralBindingDate",
+ "We're currently not able to verify any non-trivial QString "
+ "conversion that "
+ "requires QQmlStringConverters",
+ Abort);
+ },
+ [] {
+ QEXPECT_FAIL("badAttachedPropertyNested",
+ "We cannot discern between types and instances", Abort);
+ });
}
void TestQmllint::cleanQmlCode_data()
@@ -938,7 +1040,7 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("enumFromQtQml") << QStringLiteral("enumFromQtQml.qml");
QTest::newRow("anchors1") << QStringLiteral("anchors1.qml");
QTest::newRow("anchors2") << QStringLiteral("anchors2.qml");
- QTest::newRow("optionalImport") << QStringLiteral("optionalImport.qml");
+ QTest::newRow("defaultImport") << QStringLiteral("defaultImport.qml");
QTest::newRow("goodAliasObject") << QStringLiteral("goodAliasObject.qml");
QTest::newRow("jsmoduleimport") << QStringLiteral("jsmoduleimport.qml");
QTest::newRow("overridescript") << QStringLiteral("overridescript.qml");
@@ -1021,6 +1123,7 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("unexportedCppBase") << QStringLiteral("unexportedCppBase.qml");
QTest::newRow("requiredWithRootLevelAlias") << QStringLiteral("RequiredWithRootLevelAlias.qml");
QTest::newRow("jsVarDeclarations") << QStringLiteral("jsVarDeclarations.qml");
+ QTest::newRow("qmodelIndex") << QStringLiteral("qmodelIndex.qml");
}
void TestQmllint::cleanQmlCode()
@@ -1029,45 +1132,49 @@ void TestQmllint::cleanQmlCode()
QJsonArray warnings;
- callQmllint(filename, true, &warnings);
- QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson()));
+ runTest(filename, Result::clean());
}
void TestQmllint::compilerWarnings_data()
{
QTest::addColumn<QString>("filename");
- QTest::addColumn<bool>("shouldSucceed");
- QTest::addColumn<QString>("warning");
+ QTest::addColumn<Result>("result");
QTest::addColumn<bool>("enableCompilerWarnings");
- QTest::newRow("listIndices") << QStringLiteral("listIndices.qml") << true << QString() << true;
+ QTest::newRow("listIndices") << QStringLiteral("listIndices.qml") << Result::clean() << true;
QTest::newRow("lazyAndDirect")
- << QStringLiteral("LazyAndDirect/Lazy.qml") << true << QString() << true;
- QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << true << QString() << true;
- QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << true << QString()
- << true;
-
- QTest::newRow("shadowable") << QStringLiteral("shadowable.qml") << false
- << QStringLiteral("with type NotSoSimple can be shadowed") << true;
- QTest::newRow("tooFewParameters") << QStringLiteral("tooFewParams.qml") << false
- << QStringLiteral("No matching override found") << true;
+ << QStringLiteral("LazyAndDirect/Lazy.qml") << Result::clean() << true;
+ QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << Result::clean() << true;
+ QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << Result::clean() << true;
+
+ QTest::newRow("shadowable") << QStringLiteral("shadowable.qml")
+ << Result { { Message { QStringLiteral(
+ "with type NotSoSimple can be shadowed") } } }
+ << true;
+ QTest::newRow("tooFewParameters")
+ << QStringLiteral("tooFewParams.qml")
+ << Result { { Message { QStringLiteral("No matching override found") } } } << true;
QTest::newRow("javascriptVariableArgs")
- << QStringLiteral("javascriptVariableArgs.qml") << false
- << QStringLiteral("Function expects 0 arguments, but 2 were provided") << true;
+ << QStringLiteral("javascriptVariableArgs.qml")
+ << Result { { Message {
+ QStringLiteral("Function expects 0 arguments, but 2 were provided") } } }
+ << true;
QTest::newRow("unknownTypeInRegister")
- << QStringLiteral("unknownTypeInRegister.qml") << false
- << QStringLiteral("Functions without type annotations won't be compiled") << true;
- QTest::newRow("pragmaStrict") << QStringLiteral("pragmaStrict.qml") << true
- << QStringLiteral(
- "Functions without type annotations won't be compiled")
- << false;
+ << QStringLiteral("unknownTypeInRegister.qml")
+ << Result { { Message {
+ QStringLiteral("Functions without type annotations won't be compiled") } } }
+ << true;
+ QTest::newRow("pragmaStrict")
+ << QStringLiteral("pragmaStrict.qml")
+ << Result { { { QStringLiteral(
+ "Functions without type annotations won't be compiled") } } }
+ << true;
}
void TestQmllint::compilerWarnings()
{
QFETCH(QString, filename);
- QFETCH(bool, shouldSucceed);
- QFETCH(QString, warning);
+ QFETCH(Result, result);
QFETCH(bool, enableCompilerWarnings);
QJsonArray warnings;
@@ -1077,49 +1184,48 @@ void TestQmllint::compilerWarnings()
if (enableCompilerWarnings)
options[u"compiler"_qs].setLevel(u"warning"_qs);
- callQmllint(filename, shouldSucceed, &warnings, {}, {}, {}, UseDefaultImports, &options);
-
- if (!warning.isEmpty())
- searchWarnings(warnings, warning, filename);
+ runTest(filename, result, {}, {}, {}, UseDefaultImports, &options);
}
void TestQmllint::controlsSanity_data()
{
QTest::addColumn<QString>("filename");
- QTest::addColumn<QString>("warning");
- QTest::newRow("functionDeclarations") << QStringLiteral("functionDeclarations.qml")
- << QStringLiteral("Declared function \"add\"");
- QTest::newRow("signalHandlers") << QStringLiteral("signalHandlers.qml")
- << QStringLiteral("Declared signal handler \"onCompleted\"");
+ QTest::addColumn<Result>("result");
+ QTest::newRow("functionDeclarations")
+ << QStringLiteral("functionDeclarations.qml")
+ << Result { { Message { QStringLiteral("Declared function \"add\"") } } };
+ QTest::newRow("signalHandlers")
+ << QStringLiteral("signalHandlers.qml")
+ << Result { { Message { QStringLiteral("Declared signal handler \"onCompleted\"") } } };
QTest::newRow("anchors") << QStringLiteral("anchors.qml")
- << QStringLiteral("Using anchors here");
+ << Result { { Message { QStringLiteral("Using anchors here") } } };
}
void TestQmllint::controlsSanity()
{
QFETCH(QString, filename);
- QFETCH(QString, warning);
+ QFETCH(Result, result);
QJsonArray warnings;
auto options = QQmlJSLogger::options();
options[u"controls-sanity"_qs].setLevel(u"warning"_qs);
- callQmllint(filename, false, &warnings, {}, {}, {}, UseDefaultImports, &options);
-
- if (!warning.isEmpty())
- searchWarnings(warnings, warning, filename);
+ runTest(filename, result, {}, {}, {}, UseDefaultImports, &options);
}
QString TestQmllint::runQmllint(const QString &fileToLint,
std::function<void(QProcess &)> handleResult,
const QStringList &extraArgs, bool ignoreSettings,
- bool addImportDirs)
+ bool addImportDirs, bool absolutePath)
{
auto qmlImportDir = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
QStringList args;
- args << (QFileInfo(fileToLint).isAbsolute() ? fileToLint : testFile(fileToLint));
+ QString absoluteFilePath =
+ QFileInfo(fileToLint).isAbsolute() ? fileToLint : testFile(fileToLint);
+
+ args << QFileInfo(absoluteFilePath).fileName();
if (addImportDirs) {
args << QStringLiteral("-I") << qmlImportDir
@@ -1127,13 +1233,17 @@ QString TestQmllint::runQmllint(const QString &fileToLint,
}
if (ignoreSettings)
- QStringLiteral("--ignore-settings");
+ args << QStringLiteral("--ignore-settings");
+
+ if (absolutePath)
+ args << QStringLiteral("--absolute-path");
args << extraArgs;
args << QStringLiteral("--silent");
QString errors;
auto verify = [&](bool isSilent) {
QProcess process;
+ process.setWorkingDirectory(QFileInfo(absoluteFilePath).absolutePath());
process.start(m_qmllintPath, args);
handleResult(process);
errors = process.readAllStandardError();
@@ -1175,7 +1285,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint,
QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed,
const QStringList &extraArgs, bool ignoreSettings,
- bool addImportDirs)
+ bool addImportDirs, bool absolutePath)
{
return runQmllint(
fileToLint,
@@ -1188,7 +1298,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed,
else
QVERIFY(process.exitCode() != 0);
},
- extraArgs, ignoreSettings, addImportDirs);
+ extraArgs, ignoreSettings, addImportDirs, absolutePath);
}
void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings,
@@ -1214,44 +1324,112 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs
QCOMPARE(success, shouldSucceed);
}
+void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs,
+ QStringList qmltypesFiles, QStringList resources,
+ DefaultImportOption defaultImports,
+ QMap<QString, QQmlJSLogger::Option> *options)
+{
+ QJsonArray warnings;
+ callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs,
+ qmltypesFiles, resources, defaultImports, options);
+ checkResult(warnings, result);
+}
+
+template<typename ExpectedMessageFailureHandler, typename BadMessageFailureHandler>
+void TestQmllint::checkResult(const QJsonArray &warnings, const Result &result,
+ ExpectedMessageFailureHandler onExpectedMessageFailures,
+ BadMessageFailureHandler onBadMessageFailures)
+{
+ if (result.flags.testFlag(Result::Flag::NoMessages))
+ QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson()));
+
+ for (const Message &msg : result.expectedMessages) {
+ // output.contains() expect fails:
+ onExpectedMessageFailures();
+
+ searchWarnings(warnings, msg.text, msg.severity, msg.line, msg.column);
+ }
+
+ for (const Message &msg : result.badMessages) {
+ // !output.contains() expect fails:
+ onBadMessageFailures();
+
+ searchWarnings(warnings, msg.text, msg.severity, msg.line, msg.column, StringNotContained);
+ }
+
+ for (const Message &replacement : result.expectedReplacements) {
+ searchWarnings(warnings, replacement.text, replacement.severity, replacement.line,
+ replacement.column, StringContained, DoReplacementSearch);
+ }
+}
+
void TestQmllint::searchWarnings(const QJsonArray &warnings, const QString &substring,
- const QString &filename, ContainOption shouldContain,
- ReplacementOption searchReplacements)
+ QtMsgType type, quint32 line, quint32 column,
+ ContainOption shouldContain, ReplacementOption searchReplacements)
{
bool contains = false;
- auto toDisplayWarningType = [](const QString &warningType) {
- return warningType.first(1).toUpper() + warningType.mid(1);
+ auto typeStringToMsgType = [](const QString &type) -> QtMsgType {
+ if (type == u"debug")
+ return QtDebugMsg;
+ if (type == u"info")
+ return QtInfoMsg;
+ if (type == u"warning")
+ return QtWarningMsg;
+ if (type == u"critical")
+ return QtCriticalMsg;
+ if (type == u"fatal")
+ return QtFatalMsg;
+
+ Q_UNREACHABLE();
};
for (const QJsonValue &warning : warnings) {
- // We're currently recreating the output that the logger would write to the console,
- // so we can keep using our existing test data without modification.
- // In the future our test data should be replaced with a structured representation of the
- // warning it expects, possibly as a QQmlJS::DiagnosticMessage.
- const QString warningMessage =
- u"%1: %2:%3 %4"_qs
- .arg(toDisplayWarningType(warning[u"type"].toString()), testFile(filename))
- .arg(warning[u"line"].isUndefined()
- ? u""_qs
- : u"%1:%2:"_qs.arg(warning[u"line"].toInt())
- .arg(warning[u"column"].toInt()))
- .arg(warning[u"message"].toString());
+ QString warningMessage = warning[u"message"].toString();
+ quint32 warningLine = warning[u"line"].toInt();
+ quint32 warningColumn = warning[u"column"].toInt();
+ QtMsgType warningType = typeStringToMsgType(warning[u"type"].toString());
+
if (warningMessage.contains(substring)) {
+ if (warningType != type) {
+ continue;
+ }
+ if (line != 0 || column != 0) {
+ if (warningLine != line || warningColumn != column) {
+ continue;
+ }
+ }
+
contains = true;
break;
}
for (const QJsonValue &fix : warning[u"suggestions"].toArray()) {
- const QString jsonFixMessage = u"Info: "_qs + fix[u"message"].toString();
- if (jsonFixMessage.contains(substring)) {
+ const QString fixMessage = fix[u"message"].toString();
+ if (fixMessage.contains(substring)) {
contains = true;
break;
}
- if (searchReplacements == DoReplacementSearch
- && fix[u"replacement"].toString().contains(substring)) {
- contains = true;
- break;
+
+ if (searchReplacements == DoReplacementSearch) {
+ QString replacement = fix[u"replacement"].toString();
+#ifdef Q_OS_WIN
+ // Replacements can contain native line endings
+ // but we need them to be uniform in order for them to conform to our test data
+ replacement = replacement.replace(u"\r\n"_qs, u"\n"_qs);
+#endif
+
+ if (replacement.contains(substring)) {
+ quint32 fixLine = fix[u"line"].toInt();
+ quint32 fixColumn = fix[u"column"].toInt();
+ if (line != 0 || column != 0) {
+ if (fixLine != line || fixColumn != column) {
+ continue;
+ }
+ }
+ contains = true;
+ break;
+ }
}
}
}
@@ -1275,56 +1453,26 @@ void TestQmllint::searchWarnings(const QJsonArray &warnings, const QString &subs
void TestQmllint::requiredProperty()
{
- {
- QJsonArray warnings;
- callQmllint("requiredProperty.qml", true, &warnings);
- QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson()));
- }
-
- {
- QJsonArray warnings;
- callQmllint("requiredMissingProperty.qml", false, &warnings);
- searchWarnings(
- warnings,
- QStringLiteral("Property \"foo\" was marked as required but does not exist."),
- "requiredMissingProperty.qml");
- }
-
- {
- QJsonArray warnings;
- callQmllint("requiredPropertyBindings.qml", true, &warnings);
- QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson()));
- }
-
- {
- QJsonArray warnings;
- callQmllint("requiredPropertyBindingsNow.qml", false, &warnings);
-
- searchWarnings(
- warnings,
- QStringLiteral(
- "Component is missing required property required_now_string from Base"),
- "requiredPropertyBindingsNow.qml");
- searchWarnings(warnings,
- QStringLiteral("Component is missing required property "
- "required_defined_here_string from here"),
- "requiredPropertyBindingsNow.qml");
- }
-
- {
- QJsonArray warnings;
- callQmllint("requiredPropertyBindingsLater.qml", false, &warnings);
- searchWarnings(
- warnings,
- QStringLiteral("Component is missing required property required_later_string from "
- "Base (marked as required by Derived)"),
- "requiredPropertyBindingsLater.qml");
- searchWarnings(
- warnings,
- QStringLiteral("Component is missing required property required_even_later_string "
- "from Base (marked as required by here)"),
- "requiredPropertyBindingsLater.qml");
- }
+ runTest("requiredProperty.qml", Result::clean());
+
+ runTest("requiredMissingProperty.qml",
+ Result { { Message { QStringLiteral(
+ "Property \"foo\" was marked as required but does not exist.") } } });
+
+ runTest("requiredPropertyBindings.qml", Result::clean());
+ runTest("requiredPropertyBindingsNow.qml",
+ Result { { Message { QStringLiteral("Component is missing required property "
+ "required_now_string from Base") },
+ Message { QStringLiteral("Component is missing required property "
+ "required_defined_here_string from here") } } });
+ runTest("requiredPropertyBindingsLater.qml",
+ Result { { Message { QStringLiteral("Component is missing required property "
+ "required_later_string from "
+ "Base") },
+ Message { QStringLiteral("Property marked as required in Derived") },
+ Message { QStringLiteral("Component is missing required property "
+ "required_even_later_string "
+ "from Base (marked as required by here)") } } });
}
void TestQmllint::settingsFile()
@@ -1332,7 +1480,7 @@ void TestQmllint::settingsFile()
QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, QStringList(), false)
.isEmpty());
QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, QStringList(), false)
- .contains(QStringLiteral("Info: %1:2:1: Unused import at %1:2:1")
+ .contains(QStringLiteral("Warning: %1:2:1: Unused import at %1:2:1")
.arg(testFile("settings/unusedImportWarning/unused.qml"))));
QVERIFY(runQmllint("settings/bare/bare.qml", false, {}, false, false)
.contains(QStringLiteral("Failed to find the following builtins: "
@@ -1344,10 +1492,8 @@ void TestQmllint::settingsFile()
void TestQmllint::additionalImplicitImport()
{
- QJsonArray warnings;
- callQmllint("additionalImplicitImport.qml", true, &warnings, {}, {},
- { testFile("implicitImportResource.qrc") });
- QVERIFY(warnings.isEmpty());
+ runTest("additionalImplicitImport.qml", Result::clean(), {}, {},
+ { testFile("implicitImportResource.qrc") });
}
void TestQmllint::attachedPropertyReuse()
@@ -1355,24 +1501,13 @@ void TestQmllint::attachedPropertyReuse()
auto options = QQmlJSLogger::options();
options[u"multiple-attached-objects"_qs].setLevel(u"warning"_qs);
- {
- QJsonArray warnings;
-
- callQmllint("attachedPropNotReused.qml", false, &warnings, {}, {}, {}, UseDefaultImports,
- &options);
+ runTest("attachedPropNotReused.qml",
+ Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached "
+ "already initialized in a parent "
+ "scope") } } },
+ {}, {}, {}, UseDefaultImports, &options);
- searchWarnings(warnings,
- QStringLiteral("Using attached type QQuickKeyNavigationAttached "
- "already initialized in a parent "
- "scope"),
- "attachedPropNotReused.qml");
- }
- {
- QJsonArray warnings;
- callQmllint("attachedPropEnum.qml", true, &warnings, {}, {}, {}, UseDefaultImports,
- &options);
- QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson()));
- }
+ runTest("attachedPropEnum.qml", Result::clean(), {}, {}, {}, UseDefaultImports, &options);
}
void TestQmllint::missingBuiltinsNoCrash()
@@ -1391,34 +1526,24 @@ void TestQmllint::missingBuiltinsNoCrash()
QVERIFY2(jsonOutput.size() == 1, QJsonDocument(jsonOutput).toJson());
warnings = jsonOutput.at(0)[u"warnings"_qs].toArray();
- searchWarnings(warnings,
- QStringLiteral("Failed to find the following builtins: "
- "builtins.qmltypes, jsroot.qmltypes"),
- "missingBuiltinsNoCrash.qml");
+ checkResult(warnings,
+ Result { { Message { QStringLiteral("Failed to find the following builtins: "
+ "builtins.qmltypes, jsroot.qmltypes") } } });
}
void TestQmllint::absolutePath()
{
- // We cannot use the normal linter here since we need to set a different parameter in the
- // constructor
- QQmlJSLinter linter(m_defaultImportPaths, true);
-
+ QString absPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, true);
+ QString relPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, false);
const QString absolutePath = QFileInfo(testFile("memberNotFound.qml")).absoluteFilePath();
- QJsonArray jsonOutput;
- bool success = linter.lintFile(absolutePath, nullptr, true, &jsonOutput, {}, {}, {}, {});
- QVERIFY(!success);
- QVERIFY2(jsonOutput.size() == 1, QJsonDocument(jsonOutput).toJson());
- QJsonArray warnings = jsonOutput.at(0)[u"warnings"_qs].toArray();
-
- searchWarnings(warnings, absolutePath, absolutePath);
+ QVERIFY(absPathOutput.contains(absolutePath));
+ QVERIFY(!relPathOutput.contains(absolutePath));
}
void TestQmllint::importMultipartUri()
{
- QJsonArray warnings;
- callQmllint("here.qml", true, &warnings, {}, { testFile("Elsewhere/qmldir") });
- QVERIFY(warnings.isEmpty());
+ runTest("here.qml", Result::clean(), {}, { testFile("Elsewhere/qmldir") });
}
QTEST_MAIN(TestQmllint)
diff --git a/tests/auto/qml/qmltc/BLACKLIST b/tests/auto/qml/qmltc/BLACKLIST
new file mode 100644
index 0000000000..f8422a806a
--- /dev/null
+++ b/tests/auto/qml/qmltc/BLACKLIST
@@ -0,0 +1,2 @@
+[listView]
+qnx ci
diff --git a/tests/auto/qml/qmltc/data/CMakeLists.txt b/tests/auto/qml/qmltc/data/CMakeLists.txt
index 816164022e..c58378aa3f 100644
--- a/tests/auto/qml/qmltc/data/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/data/CMakeLists.txt
@@ -8,6 +8,8 @@ set(cpp_sources
cpptypes/private/testprivateproperty_p.h
cpptypes/typewithproperties.h cpptypes/typewithproperties.cpp
+ # deferred:
+ cpptypes/deferredpropertytypes.h cpptypes/deferredpropertytypes.cpp
)
set(qml_sources
@@ -65,6 +67,10 @@ set(qml_sources
privatePropertySubclass.qml
calqlatrBits.qml
propertyChangeAndSignalHandlers.qml
+ deferredProperties.qml
+ deferredProperties_group.qml
+ deferredProperties_attached.qml
+ deferredProperties_complex.qml
# support types:
DefaultPropertySingleChild.qml
@@ -108,6 +114,8 @@ qt6_add_qml_module(qmltc_test_module
QML_FILES
${qml_sources}
${js_sources}
+ DEPENDENCIES
+ QtQuick
)
qt_internal_target_compile_qml_to_cpp(qmltc_test_module
NAMESPACE QmltcTest
diff --git a/tools/qmltc/prototype/visitor.h b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp
index 730021b66c..4f66172cca 100644
--- a/tools/qmltc/prototype/visitor.h
+++ b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the tools applications of the Qt Toolkit.
+** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
@@ -26,30 +26,25 @@
**
****************************************************************************/
-#ifndef VISITOR_H
-#define VISITOR_H
+#include "deferredpropertytypes.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 <private/qqmljsimportvisitor_p.h>
+QQuickItem *TypeWithDeferredProperty::deferredProperty() const
+{
+ return m_deferredProperty;
+}
-namespace Qmltc {
-class Visitor : public QQmlJSImportVisitor
+void TypeWithDeferredProperty::setDeferredProperty(QQuickItem *value)
{
-public:
- Visitor(QQmlJSImporter *importer, QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmltypesFiles = QStringList());
+ if (m_deferredProperty != value)
+ m_deferredProperty = value;
+}
- bool visit(QQmlJS::AST::UiInlineComponent *) override;
-};
+QBindable<QQuickItem *> TypeWithDeferredProperty::bindableDeferredProperty()
+{
+ return QBindable<QQuickItem *>(&m_deferredProperty);
}
-#endif // VISITOR_H
+TestTypeAttachedWithDeferred *DeferredAttached::qmlAttachedProperties(QObject *parent)
+{
+ return new TestTypeAttachedWithDeferred(parent);
+}
diff --git a/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h
new file mode 100644
index 0000000000..24d7d46aaa
--- /dev/null
+++ b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#ifndef DEFERREDPROPERTYTYPES_H
+#define DEFERREDPROPERTYTYPES_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqmlregistration.h>
+#include <QtQuick/qquickitem.h>
+
+#include "testgroupedtype.h"
+#include "testattachedtype.h"
+
+// normal properties:
+
+class TypeWithDeferredProperty : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_CLASSINFO("DeferredPropertyNames", "deferredProperty")
+
+ Q_PROPERTY(QQuickItem *deferredProperty READ deferredProperty WRITE setDeferredProperty BINDABLE
+ bindableDeferredProperty)
+
+ QProperty<QQuickItem *> m_deferredProperty { nullptr };
+
+public:
+ TypeWithDeferredProperty(QObject *parent = nullptr) : QObject(parent) { }
+
+ QQuickItem *deferredProperty() const;
+ void setDeferredProperty(QQuickItem *);
+ QBindable<QQuickItem *> bindableDeferredProperty();
+};
+
+// group properties:
+
+class TestTypeGroupedWithDeferred : public TestTypeGrouped
+{
+ Q_OBJECT
+ Q_PROPERTY(int deferred READ getDeferred WRITE setDeferred BINDABLE bindableDeferred)
+ QML_ANONYMOUS
+ Q_CLASSINFO("DeferredPropertyNames", "deferred")
+
+ QProperty<int> m_deferred { 0 };
+
+public:
+ int getDeferred() const { return m_deferred; }
+ void setDeferred(int v) { m_deferred = v; }
+ QBindable<int> bindableDeferred() const { return QBindable<int>(&m_deferred); }
+};
+
+class TypeWithDeferredGroup : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(TestTypeGroupedWithDeferred *group READ getGroup)
+
+ TestTypeGroupedWithDeferred m_group;
+
+public:
+ TypeWithDeferredGroup(QObject *parent = nullptr) : QObject(parent) { }
+
+ TestTypeGroupedWithDeferred *getGroup() { return &m_group; }
+};
+
+// attached properties:
+
+class TestTypeAttachedWithDeferred : public TestTypeAttached
+{
+ Q_OBJECT
+ Q_PROPERTY(int deferred READ getDeferred WRITE setDeferred BINDABLE bindableDeferred)
+ QML_ANONYMOUS
+ Q_CLASSINFO("DeferredPropertyNames", "deferred")
+
+ QProperty<int> m_deferred { 0 };
+
+public:
+ TestTypeAttachedWithDeferred(QObject *parent) : TestTypeAttached(parent) { }
+
+ int getDeferred() const { return m_deferred; }
+ void setDeferred(int v) { m_deferred = v; }
+ QBindable<int> bindableDeferred() const { return QBindable<int>(&m_deferred); }
+};
+
+class DeferredAttached : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ATTACHED(TestTypeAttachedWithDeferred)
+
+public:
+ DeferredAttached(QObject *parent = nullptr) : QObject(parent) { }
+
+ static TestTypeAttachedWithDeferred *qmlAttachedProperties(QObject *);
+};
+
+// special:
+
+class TypeWithDeferredComplexProperties : public TypeWithDeferredGroup
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_CLASSINFO("DeferredPropertyNames", "group,DeferredAttached")
+
+public:
+ TypeWithDeferredComplexProperties(QObject *parent = nullptr) : TypeWithDeferredGroup(parent) { }
+};
+
+#endif // DEFERREDPROPERTYTYPES_H
diff --git a/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h b/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h
index 51d7eca015..64625103f4 100644
--- a/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h
+++ b/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h
@@ -26,6 +26,9 @@
**
****************************************************************************/
+#ifndef TYPEWITHPROPERTIES_H
+#define TYPEWITHPROPERTIES_H
+
#include <QtCore/qobject.h>
#include <QtCore/qproperty.h>
#include <QtCore/qstring.h>
@@ -70,3 +73,5 @@ Q_SIGNALS:
void cWeirdSignal(QVariant);
void dSignal(QString, int);
};
+
+#endif // TYPEWITHPROPERTIES_H
diff --git a/tests/auto/qml/qmltc/data/deferredProperties.qml b/tests/auto/qml/qmltc/data/deferredProperties.qml
new file mode 100644
index 0000000000..7b977744e2
--- /dev/null
+++ b/tests/auto/qml/qmltc/data/deferredProperties.qml
@@ -0,0 +1,16 @@
+import QtQuick
+import QmltcTests 1.0
+TypeWithDeferredProperty {
+ id: root
+ property int width: 42
+
+ deferredProperty: Rectangle {
+ width: root.width * 2
+ implicitHeight: 4
+ height: implicitHeight
+ Rectangle {
+ width: root.width // + parent.width
+ height: parent.height
+ }
+ }
+}
diff --git a/tests/auto/qml/qmltc/data/deferredProperties_attached.qml b/tests/auto/qml/qmltc/data/deferredProperties_attached.qml
new file mode 100644
index 0000000000..875deb5778
--- /dev/null
+++ b/tests/auto/qml/qmltc/data/deferredProperties_attached.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import QmltcTests 1.0
+QtObject {
+ DeferredAttached.attachedFormula: 43 + 10 - (5 * 2)
+ DeferredAttached.deferred: 42
+}
diff --git a/tests/auto/qml/qmltc/data/deferredProperties_complex.qml b/tests/auto/qml/qmltc/data/deferredProperties_complex.qml
new file mode 100644
index 0000000000..ce5429a095
--- /dev/null
+++ b/tests/auto/qml/qmltc/data/deferredProperties_complex.qml
@@ -0,0 +1,9 @@
+import QtQuick
+import QmltcTests 1.0
+TypeWithDeferredComplexProperties {
+ group.str: "still immediate"
+ group.deferred: -1
+
+ DeferredAttached.attachedFormula: Math.abs(10 * 2)
+ DeferredAttached.deferred: 100
+}
diff --git a/tests/auto/qml/qmltc/data/deferredProperties_group.qml b/tests/auto/qml/qmltc/data/deferredProperties_group.qml
new file mode 100644
index 0000000000..a162968c86
--- /dev/null
+++ b/tests/auto/qml/qmltc/data/deferredProperties_group.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import QmltcTests 1.0
+TypeWithDeferredGroup {
+ group.str: "foo" + "bar"
+ group.deferred: 42
+}
diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp
index e26aa06e78..ef92dd5c74 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.cpp
+++ b/tests/auto/qml/qmltc/tst_qmltc.cpp
@@ -40,6 +40,10 @@
#include "importnamespace.h"
#include "componenttype.h"
#include "componenttypes.h"
+#include "deferredproperties.h"
+#include "deferredproperties_group.h"
+#include "deferredproperties_attached.h"
+#include "deferredproperties_complex.h"
#include "signalhandlers.h"
#include "javascriptfunctions.h"
@@ -82,6 +86,8 @@
#include "calqlatrbits.h"
#include "propertychangeandsignalhandlers.h"
+#include "testprivateproperty.h"
+
// Qt:
#include <QtCore/qstring.h>
#include <QtCore/qbytearray.h>
@@ -94,6 +100,7 @@
#include <QtTest/qsignalspy.h>
+#include <QtCore/private/qobject_p.h>
#include <QtTest/private/qemulationdetector_p.h>
#ifndef QMLTC_TESTS_DISABLE_CACHE
@@ -128,6 +135,10 @@ void tst_qmltc::initTestCase()
QUrl("qrc:/QmltcTests/ObjectWithId.qml"),
QUrl("qrc:/QmltcTests/documentWithIds.qml"),
QUrl("qrc:/QmltcTests/importNamespace.qml"),
+ QUrl("qrc:/QmltcTests/deferredProperties.qml"),
+ QUrl("qrc:/QmltcTests/deferredProperties_group.qml"),
+ QUrl("qrc:/QmltcTests/deferredProperties_attached.qml"),
+ QUrl("qrc:/QmltcTests/deferredProperties_complex.qml"),
QUrl("qrc:/QmltcTests/signalHandlers.qml"),
QUrl("qrc:/QmltcTests/javaScriptFunctions.qml"),
@@ -581,6 +592,73 @@ void tst_qmltc::componentTypes()
}
}
+void tst_qmltc::deferredProperties()
+{
+ {
+ QQmlEngine e;
+ PREPEND_NAMESPACE(deferredProperties) created(&e);
+ QVERIFY(created.deferredProperty()
+ == nullptr); // binding is not applied since it is deferred
+
+ qmlExecuteDeferred(&created);
+
+ QQuickRectangle *rect = qobject_cast<QQuickRectangle *>(created.deferredProperty());
+ QVERIFY(rect);
+ QCOMPARE(rect->width(), created.width() * 2);
+ QCOMPARE(rect->implicitHeight(), 4);
+ QCOMPARE(rect->height(), rect->implicitHeight());
+
+ QQmlListReference children(rect, "data");
+ QCOMPARE(children.size(), 1);
+ QQuickRectangle *subRect = qobject_cast<QQuickRectangle *>(children.at(0));
+ QVERIFY(subRect);
+ QCOMPARE(subRect->width(), created.width());
+ QCOMPARE(subRect->height(), rect->height());
+ }
+ {
+ QQmlEngine e;
+ PREPEND_NAMESPACE(deferredProperties_group) created(&e);
+ QCOMPARE(created.getGroup()->getStr(), u"foobar"_qs);
+ QCOMPARE(created.getGroup()->getDeferred(), 0);
+ // Note: we can't easily evaluate a deferred binding for a
+ // `group.deferred` here, so just accept the fact the the value is not
+ // set at all as a successful test
+ }
+ {
+ QQmlEngine e;
+ PREPEND_NAMESPACE(deferredProperties_attached) created(&e);
+ TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>(
+ qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
+ QVERIFY(attached);
+
+ QCOMPARE(attached->getAttachedFormula(), 43);
+ QCOMPARE(attached->getDeferred(), 0);
+ // Note: we can't easily evaluate a deferred binding for a
+ // `group.deferred` here, so just accept the fact the the value is not
+ // set at all as a successful test
+ }
+ {
+ QQmlEngine e;
+ PREPEND_NAMESPACE(deferredProperties_complex) created(&e);
+
+ // `group` binding is not deferred as per current behavior outside of
+ // PropertyChanges and friends. we defer `group.deferred` binding though
+ QCOMPARE(created.getGroup()->getStr(), u"still immediate"_qs);
+ QCOMPARE(created.getGroup()->getDeferred(), 0);
+
+ QVERIFY(!qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
+
+ qmlExecuteDeferred(&created);
+
+ TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>(
+ qmlAttachedPropertiesObject<DeferredAttached>(&created, false));
+ QVERIFY(attached);
+
+ QCOMPARE(attached->getAttachedFormula(), 20);
+ QCOMPARE(attached->getDeferred(), 100);
+ }
+}
+
void tst_qmltc::signalHandlers()
{
QQmlEngine e;
@@ -1102,11 +1180,9 @@ void tst_qmltc::defaultAlias()
QSKIP("Not implemented - not supported");
}
-// TODO: this just doesn't work currently
void tst_qmltc::attachedProperty()
{
QQmlEngine e;
- QSKIP("Broken in many ways.");
PREPEND_NAMESPACE(attachedProperty) created(&e);
TestTypeAttached *attached = qobject_cast<TestTypeAttached *>(
@@ -1145,8 +1221,6 @@ void tst_qmltc::attachedProperty()
void tst_qmltc::groupedProperty()
{
QQmlEngine e;
- QSKIP("Property index is wrong due to not picking QtQml dependency when creating group "
- "property scope");
PREPEND_NAMESPACE(groupedProperty) created(&e);
TestTypeGrouped *grouped = created.getGroup();
@@ -1708,9 +1782,22 @@ void tst_qmltc::keyEvents()
void tst_qmltc::privateProperties()
{
- QSKIP("The same problem with poor QObject qmltypes is encountered here.");
QQmlEngine e;
PREPEND_NAMESPACE(privatePropertySubclass) created(&e);
+ QCOMPARE(created.dummy(), u"bar"_qs);
+ QCOMPARE(created.strAlias(), u"foobar"_qs);
+ QCOMPARE(created.smthAlias(), 42);
+
+ auto privateCreated = static_cast<PrivatePropertyTypePrivate *>(QObjectPrivate::get(&created));
+ QVERIFY(privateCreated);
+ QCOMPARE(privateCreated->foo(), u"Smth is: 42"_qs);
+
+ ValueTypeGroup vt = privateCreated->vt();
+ QCOMPARE(vt.count(), 11);
+
+ TestTypeGrouped *group = privateCreated->getGroup();
+ QCOMPARE(group->getCount(), 43);
+ QCOMPARE(group->getStr(), created.strAlias());
}
void tst_qmltc::calqlatrBits()
diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h
index ffffc6d9b7..5ce8a189da 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.h
+++ b/tests/auto/qml/qmltc/tst_qmltc.h
@@ -53,6 +53,7 @@ private slots:
void ids();
void importNamespace();
void componentTypes();
+ void deferredProperties();
void signalHandlers();
void jsFunctions();
diff --git a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
index 8b0db1c50a..d8c08ee5be 100644
--- a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
+++ b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
@@ -49,6 +49,7 @@
#include <private/qquickitem_p.h>
#include <private/qv4qmlcontext_p.h>
#include <private/qqmlproperty_p.h>
+#include <private/qquickanchors_p.h>
#include <array>
#include <memory>
diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
index 46f1d856d3..bd70d2fae7 100644
--- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
@@ -12,7 +12,12 @@ qt6_add_qml_module(tst_qmltc_qprocess
QML_FILES
data/dummy.qml
data/inlineComponent.qml
+ data/SingletonThing.qml
+ data/erroneousFile.qml
)
+# special setup for singleton files:
+set_source_files_properties(SingletonThing.qml PROPERTIES QT_QML_SINGLETON_TYPE true)
+
add_dependencies(tst_qmltc_qprocess Qt::qmltc)
# fetch --resource arguments manually (mimics the logic of qmltc compilation
diff --git a/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml b/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml
new file mode 100644
index 0000000000..599b54eddd
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQml
+
+QtObject {
+ property int integerProperty: 42
+ property string stringProperty: "hello"
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml b/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml
new file mode 100644
index 0000000000..a7316ed78b
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml
@@ -0,0 +1,4 @@
+import QtQml
+QtObject {
+ property NonExistent type: QtObject {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index 50abcc8332..09c77ce37a 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -68,6 +68,8 @@ private slots:
void noBuiltins();
void noQtQml();
void inlineComponent();
+ void singleton();
+ void warningsAsErrors();
};
#ifndef TST_QMLTC_QPROCESS_RESOURCES
@@ -200,5 +202,18 @@ void tst_qmltc_qprocess::inlineComponent()
QVERIFY(!errors.contains(u"Inline components are not supported"_qs));
}
+void tst_qmltc_qprocess::singleton()
+{
+ const auto errors = runQmltc(u"SingletonThing.qml"_qs, false);
+ QEXPECT_FAIL("", "qmltc does not support singletons at the moment", Continue);
+ QVERIFY(!errors.contains(u"Singleton types are not supported"_qs));
+}
+
+void tst_qmltc_qprocess::warningsAsErrors()
+{
+ const auto errors = runQmltc(u"erroneousFile.qml"_qs, false);
+ QVERIFY2(errors.contains(u"Error:"_qs), qPrintable(errors)); // Note: not a warning!
+}
+
QTEST_MAIN(tst_qmltc_qprocess)
#include "tst_qmltc_qprocess.moc"
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index c7326c30c4..ad22f51cd9 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -62,3 +62,13 @@ set_target_properties(tst_qmltyperegistrar PROPERTIES
# yet, so we have to call it directly to test that code path for now.
_qt_internal_qml_type_registration(tst_qmltyperegistrar MANUAL_MOC_JSON_FILES ${json_list})
add_subdirectory(foreign)
+
+qt_add_library(tst-qmltyperegistrar-with-dashes)
+target_link_libraries(tst-qmltyperegistrar-with-dashes PRIVATE Qt::Core Qt::Qml)
+qt_enable_autogen_tool(tst-qmltyperegistrar-with-dashes "moc" ON)
+qt_add_qml_module(tst-qmltyperegistrar-with-dashes
+ URI Module-With-Dashes
+ VERSION 1.0
+ SOURCES
+ foo.cpp foo.h
+)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 5619c2aa0f..3fc746552e 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -339,6 +339,46 @@ void tst_qmltyperegistrar::methodReturnType()
QVERIFY(qmltypesData.contains("type: \"QQmlComponent\""));
}
+void tst_qmltyperegistrar::addRemoveVersion_data()
+{
+ QTest::addColumn<QTypeRevision>("importVersion");
+ for (int i = 0; i < 20; ++i)
+ QTest::addRow("v1.%d.qml", i) << QTypeRevision::fromVersion(1, i);
+}
+
+void tst_qmltyperegistrar::addRemoveVersion()
+{
+ QFETCH(QTypeRevision, importVersion);
+
+ const bool creatable
+ = importVersion > QTypeRevision::fromVersion(1, 2)
+ && importVersion < QTypeRevision::fromVersion(1, 18);
+ const bool thingAccessible = importVersion > QTypeRevision::fromVersion(1, 3);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral("import QmlTypeRegistrarTest %1.%2\n"
+ "Versioned {\n"
+ " property int thing: revisioned\n"
+ "}")
+ .arg(importVersion.majorVersion()).arg(importVersion.minorVersion()).toUtf8(),
+ QUrl(QTest::currentDataTag()));
+ if (!creatable) {
+ QVERIFY(c.isError());
+ return;
+ }
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ if (!thingAccessible) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(QStringLiteral("%1:3: ReferenceError: revisioned is not defined")
+ .arg(QTest::currentDataTag())));
+ }
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("thing").toInt(), thingAccessible ? 24 : 0);
+}
+
#ifdef QT_QUICK_LIB
void tst_qmltyperegistrar::foreignRevisionedProperty()
{
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index d57053a051..385d3b6666 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -469,6 +469,28 @@ public:
};
#endif
+class AddedInLateVersion : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Versioned)
+ QML_ADDED_IN_VERSION(1, 8)
+ Q_PROPERTY(int revisioned READ revisioned CONSTANT REVISION(1, 4))
+ Q_PROPERTY(int insane READ revisioned CONSTANT REVISION 17)
+public:
+ AddedInLateVersion(QObject *parent = nullptr) : QObject(parent) {}
+ int revisioned() const { return 24; }
+};
+
+class RemovedInEarlyVersion : public AddedInLateVersion
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Versioned)
+ QML_ADDED_IN_VERSION(1, 3)
+ QML_REMOVED_IN_VERSION(1, 8)
+public:
+ RemovedInEarlyVersion(QObject *parent = nullptr) : AddedInLateVersion(parent) {}
+};
+
class tst_qmltyperegistrar : public QObject
{
Q_OBJECT
@@ -508,6 +530,9 @@ private slots:
void foreignRevisionedProperty();
#endif
+ void addRemoveVersion_data();
+ void addRemoveVersion();
+
private:
QByteArray qmltypesData;
};
diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
index 7f05a759b6..d9fbf6517d 100644
--- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
@@ -35,6 +35,9 @@ qt_internal_add_resource(tst_qqmlapplicationengine "tst_qqmlapplicationengine"
${tst_qqmlapplicationengine_resource_files}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlapplicationengine)
+endif()
#### Keys ignored in scope 2:.:.:tst_qqmlapplicationengine.pro:<TRUE>:
# TRANSLATIONS = "data/i18n/qml_ja.ts"
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index 67c75ccce7..c948ad5b9c 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -101,6 +101,10 @@ void tst_qqmlapplicationengine::basicLoading()
// will break.
void tst_qqmlapplicationengine::testNonResolvedPath()
{
+#if defined(Q_OS_INTEGRITY)
+ QSKIP("INTEGRITY stores QML files in resources, and the path to a resource cannot be relative in this case");
+#endif
+
#ifdef Q_OS_ANDROID
QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case");
#endif
diff --git a/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs b/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs
index 3dd3507d45..9175ba0a67 100644
--- a/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs
+++ b/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs
@@ -1,5 +1,6 @@
export function withProp(root) {
- const component = Qt.createComponent("data/jsmodule/Dynamic.qml");
+ const prefix = Qt.platform.os == "android" ? "qrc:" : "";
+ const component = Qt.createComponent(prefix + "data/jsmodule/Dynamic.qml");
const el = component.createObject(root, { value: 42 });
return el.value;
}
diff --git a/tests/auto/qml/qqmlconsole/CMakeLists.txt b/tests/auto/qml/qqmlconsole/CMakeLists.txt
index 171d1ad578..5096503c15 100644
--- a/tests/auto/qml/qqmlconsole/CMakeLists.txt
+++ b/tests/auto/qml/qqmlconsole/CMakeLists.txt
@@ -21,6 +21,10 @@ qt_internal_add_test(tst_qqmlconsole
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlconsole)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt
index 01e9999f79..43fec7cb90 100644
--- a/tests/auto/qml/qqmlengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlengine/CMakeLists.txt
@@ -36,6 +36,9 @@ qt_internal_add_resource(tst_qqmlengine "qmake_immediate"
${qmake_immediate_resource_files}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlengine)
+endif()
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlfileselector/CMakeLists.txt b/tests/auto/qml/qqmlfileselector/CMakeLists.txt
index d010280a9d..4d7c4f840b 100644
--- a/tests/auto/qml/qqmlfileselector/CMakeLists.txt
+++ b/tests/auto/qml/qqmlfileselector/CMakeLists.txt
@@ -22,6 +22,10 @@ qt_internal_add_test(tst_qqmlfileselector
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlfileselector)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlimport/data/absoluteImport.qml b/tests/auto/qml/qqmlimport/data/absoluteImport.qml
new file mode 100644
index 0000000000..d182fe5b71
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/absoluteImport.qml
@@ -0,0 +1,4 @@
+import "/foo/bar/baz"
+
+QtObject {}
+
diff --git a/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml b/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml
new file mode 100644
index 0000000000..74b98664aa
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml
@@ -0,0 +1,4 @@
+import ":/absolute/resource/path"
+
+QtObject {}
+
diff --git a/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml b/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml
new file mode 100644
index 0000000000..6a7e2b7ea5
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml
@@ -0,0 +1,4 @@
+import ":relative/resource/path"
+
+QtObject {}
+
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index f52b01cf04..a2662d5a67 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -35,6 +35,7 @@
#include <private/qqmlimport_p.h>
#include <private/qqmlengine_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QQmlComponent>
class tst_QQmlImport : public QQmlDataTest
{
@@ -59,6 +60,8 @@ private slots:
void cleanup();
void envResourceImportPath();
void preferResourcePath();
+ void invalidFileImport_data();
+ void invalidFileImport();
};
void tst_QQmlImport::cleanup()
@@ -104,6 +107,36 @@ void tst_QQmlImport::preferResourcePath()
QCOMPARE(o->objectName(), "right");
}
+void tst_QQmlImport::invalidFileImport_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QString>("import");
+ QTest::addRow("file absolute")
+ << QStringLiteral("absoluteImport.qml")
+ << QStringLiteral("/foo/bar/baz");
+ QTest::addRow("resource absolute")
+ << QStringLiteral("absoluteResourceImport.qml")
+ << QStringLiteral(":/absolute/resource/path");
+ QTest::addRow("resource relative")
+ << QStringLiteral("relativeResourceImport.qml")
+ << QStringLiteral(":relative/resource/path");
+}
+
+void tst_QQmlImport::invalidFileImport()
+{
+ QFETCH(QString, file);
+ QFETCH(QString, import);
+
+ QQmlEngine engine;
+
+ QQmlComponent component(&engine, testFileUrl(file));
+ QVERIFY(component.isError());
+ QVERIFY(component.errorString().contains(
+ QStringLiteral("\"%1\" is not a valid import URL. "
+ "You can pass relative paths or URLs with schema, "
+ "but not absolute paths or resource paths.").arg(import)));
+}
+
void tst_QQmlImport::testDesignerSupported()
{
QQuickView *window = new QQuickView();
diff --git a/tests/auto/qml/qqmlincubator/CMakeLists.txt b/tests/auto/qml/qqmlincubator/CMakeLists.txt
index 934199a66e..b2ade30c39 100644
--- a/tests/auto/qml/qqmlincubator/CMakeLists.txt
+++ b/tests/auto/qml/qqmlincubator/CMakeLists.txt
@@ -24,6 +24,10 @@ qt_internal_add_test(tst_qqmlincubator
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlincubator)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlinfo/CMakeLists.txt b/tests/auto/qml/qqmlinfo/CMakeLists.txt
index 9ba8ca4c4c..3020b84c59 100644
--- a/tests/auto/qml/qqmlinfo/CMakeLists.txt
+++ b/tests/auto/qml/qqmlinfo/CMakeLists.txt
@@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmlinfo
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlinfo)
+endif()
+
#### Keys ignored in scope 1:.:.:qqmlinfo.pro:<TRUE>:
# QML_IMPORT_MAJOR_VERSION = "1"
# QML_IMPORT_NAME = "org.qtproject.Test"
diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
index 0d6e3624cb..2756f04120 100644
--- a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
+++ b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
@@ -1,3 +1,4 @@
+import QtQml 2.0
import Test 1.0
ItemModelsTest {
diff --git a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
index 13037065a6..85987bdcac 100644
--- a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
+++ b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
@@ -1,3 +1,4 @@
+import QtQml 2.0
import Test 1.0
ItemModelsTest {
diff --git a/tests/auto/qml/qqmllanguage/data/foreignExtended.qml b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml
index 4863e0d567..b01af6d229 100644
--- a/tests/auto/qml/qqmllanguage/data/foreignExtended.qml
+++ b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml
@@ -5,12 +5,17 @@ QtObject {
property Foreign foreign: Foreign {
objectName: "foreign"
}
- property Extended extended: Extended {}
+ property Extended extended: Extended {
+ objectName: "extended"
+ property int changeCount: 0
+ onExtensionChanged: ++changeCount
+ }
property ForeignExtended foreignExtended: ForeignExtended {
objectName: "foreignExtended"
}
property int extendedBase: extended.base
+ property int extendedChangeCount: extended.changeCount
property int extendedInvokable: extended.invokable()
property int extendedSlot: extended.slot()
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 57d3fe8860..b09840606b 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1470,13 +1470,23 @@ public:
class Extension : public QObject
{
Q_OBJECT
- Q_PROPERTY(int extension READ extension CONSTANT)
+ Q_PROPERTY(int extension READ extension WRITE setExtension NOTIFY extensionChanged FINAL)
public:
Extension(QObject *parent = nullptr) : QObject(parent) {}
- int extension() const { return 42; }
+ int extension() const { return ext; }
+ void setExtension(int e) {
+ if (e != ext) {
+ ext = e;
+ emit extensionChanged();
+ }
+ }
Q_INVOKABLE int invokable() { return 123; }
+Q_SIGNALS:
+ void extensionChanged();
public slots:
int slot() { return 456; }
+private:
+ int ext = 42;
};
class Extended : public QObject
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 17d7ff6f7e..cf35ad0a6a 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -283,6 +283,7 @@ private slots:
void lazyDeferredSubObject();
void deferredProperties();
void executeDeferredPropertiesOnce();
+ void deferredProperties_extra();
void noChildEvents();
@@ -5134,6 +5135,82 @@ void tst_qqmllanguage::executeDeferredPropertiesOnce()
QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true));
}
+class GroupType : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int foo READ getFoo WRITE setFoo BINDABLE bindableFoo)
+ Q_PROPERTY(int bar READ getBar WRITE setBar BINDABLE bindableBar)
+ Q_CLASSINFO("DeferredPropertyNames", "bar")
+
+ QProperty<int> m_foo { 0 };
+ QProperty<int> m_bar { 0 };
+
+public:
+ int getFoo() const { return m_foo; }
+ void setFoo(int v) { m_foo = v; }
+ QBindable<int> bindableFoo() const { return QBindable<int>(&m_foo); }
+
+ int getBar() const { return m_bar; }
+ void setBar(int v) { m_bar = v; }
+ QBindable<int> bindableBar() const { return QBindable<int>(&m_bar); }
+};
+
+class ExtraDeferredProperties : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(GroupType *group READ getGroup)
+ Q_CLASSINFO("DeferredPropertyNames", "group,MyQmlObject")
+
+ GroupType m_group;
+
+public:
+ ExtraDeferredProperties(QObject *parent = nullptr) : QObject(parent) { }
+
+ GroupType *getGroup() { return &m_group; }
+};
+
+void tst_qqmllanguage::deferredProperties_extra()
+{
+ // Note: because ExtraDeferredProperties defers only a `group` property, the
+ // deferral does not actually work.
+ QTest::ignoreMessage(
+ QtMsgType::QtWarningMsg,
+ "Binding on group is not deferred as requested by the DeferredPropertyNames class info "
+ "because it constitutes a group property.");
+
+ qmlRegisterType<GroupType>("deferred.properties.extra", 1, 0, "GroupType");
+ qmlRegisterType<ExtraDeferredProperties>("deferred.properties.extra", 1, 0,
+ "ExtraDeferredProperties");
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQuick
+ import Test 1.0
+ import deferred.properties.extra 1.0
+
+ ExtraDeferredProperties {
+ group.foo: 4
+ group.bar: 4
+ MyQmlObject.value: 1
+ }
+ )", QUrl());
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<ExtraDeferredProperties> object(
+ qobject_cast<ExtraDeferredProperties *>(component.create()));
+ QVERIFY(object);
+
+ QCOMPARE(object->getGroup()->getFoo(), 4); // not deferred (as group itself is not deferred)
+ QCOMPARE(object->getGroup()->getBar(), 0); // deferred, as per group's own deferred names
+ // but attached property is deferred:
+ QVERIFY(!qmlAttachedPropertiesObject<MyQmlObject>(object.get(), false));
+
+ qmlExecuteDeferred(object.get());
+
+ auto attached = qmlAttachedPropertiesObject<MyQmlObject>(object.get(), false);
+ QVERIFY(attached);
+ QCOMPARE(attached->property("value").toInt(), 1);
+}
+
void tst_qqmllanguage::noChildEvents()
{
QQmlComponent component(&engine);
@@ -5559,9 +5636,20 @@ void tst_qqmllanguage::extendedForeignTypes()
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
+ QObject *extended = o->property("extended").value<QObject *>();
+ QVERIFY(extended);
+ QSignalSpy extensionChangedSpy(extended, SIGNAL(extensionChanged()));
+
QCOMPARE(o->property("extendedBase").toInt(), 43);
QCOMPARE(o->property("extendedExtension").toInt(), 42);
QCOMPARE(o->property("foreignExtendedExtension").toInt(), 42);
+
+ QCOMPARE(extensionChangedSpy.count(), 0);
+ extended->setProperty("extension", 44);
+ QCOMPARE(extensionChangedSpy.count(), 1);
+ QCOMPARE(o->property("extendedChangeCount").toInt(), 1);
+ QCOMPARE(o->property("extendedExtension").toInt(), 44);
+
QCOMPARE(o->property("foreignObjectName").toString(), QLatin1String("foreign"));
QCOMPARE(o->property("foreignExtendedObjectName").toString(), QLatin1String("foreignExtended"));
QCOMPARE(o->property("extendedInvokable").toInt(), 123);
diff --git a/tests/auto/qml/qqmllistreference/CMakeLists.txt b/tests/auto/qml/qqmllistreference/CMakeLists.txt
index b620873c54..9d4b3cac7a 100644
--- a/tests/auto/qml/qqmllistreference/CMakeLists.txt
+++ b/tests/auto/qml/qqmllistreference/CMakeLists.txt
@@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmllistreference
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmllistreference)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
index a46b58f9ab..93edb03527 100644
--- a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
+++ b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
@@ -21,6 +21,10 @@ qt_internal_add_test(tst_qqmlmetaobject
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlmetaobject)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
index 81c6b0d2a1..fa54d440b8 100644
--- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
+++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include <QtTest/QtTest>
+#include <QtCore/QScopedPointer>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -188,8 +189,9 @@ void tst_QQmlMetaObject::property()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl(testFile));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ QObject *object = obj.get();
const QMetaObject *mo = object->metaObject();
QVERIFY(mo->superClass() != nullptr);
@@ -263,8 +265,6 @@ void tst_QQmlMetaObject::property()
QVERIFY(!prop.write(object, prop.read(object)));
QCOMPARE(changedSpy.count(), 0);
}
-
- delete object;
}
void tst_QQmlMetaObject::method_data()
@@ -359,8 +359,9 @@ void tst_QQmlMetaObject::method()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl(testFile));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ QObject *object = obj.get();
const QMetaObject *mo = object->metaObject();
QVERIFY(mo->superClass() != nullptr);
@@ -384,8 +385,6 @@ void tst_QQmlMetaObject::method()
QCOMPARE(QString::fromUtf8(method.typeName()), returnTypeName);
QCOMPARE(method.returnType(), returnType);
-
- delete object;
}
QTEST_MAIN(tst_QQmlMetaObject)
diff --git a/tests/auto/qml/qqmlmetatype/CMakeLists.txt b/tests/auto/qml/qqmlmetatype/CMakeLists.txt
index 86b1fa6afb..74146bb49b 100644
--- a/tests/auto/qml/qqmlmetatype/CMakeLists.txt
+++ b/tests/auto/qml/qqmlmetatype/CMakeLists.txt
@@ -46,6 +46,9 @@ qt_internal_add_resource(tst_qqmlmetatype "qmake_qmldirresource"
${qmake_qmldirresource_resource_files}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlmetatype)
+endif()
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlnotifier/CMakeLists.txt b/tests/auto/qml/qqmlnotifier/CMakeLists.txt
index dfd9d625bc..4056f06458 100644
--- a/tests/auto/qml/qqmlnotifier/CMakeLists.txt
+++ b/tests/auto/qml/qqmlnotifier/CMakeLists.txt
@@ -20,6 +20,10 @@ qt_internal_add_test(tst_qqmlnotifier
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlnotifier)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlpromise/CMakeLists.txt b/tests/auto/qml/qqmlpromise/CMakeLists.txt
index 00c3dcf6b4..ac0ffebabe 100644
--- a/tests/auto/qml/qqmlpromise/CMakeLists.txt
+++ b/tests/auto/qml/qqmlpromise/CMakeLists.txt
@@ -24,6 +24,10 @@ qt_internal_add_test(tst_qqmlpromise
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlpromise)
+endif()
+
#### Keys ignored in scope 1:.:.:qqmlpromise.pro:<TRUE>:
# DISTFILES = "data/then-fulfilled-non-callable.qml" "data/then-reject-non-callable.qml" "data/then-resolve-multiple-then.qml" "data/then-resolve-chaining.qml" "data/then-reject-chaining.qml" "data/promise-resolve-with-value.qml" "data/promise-resolve-with-promise.qml" "data/promise-reject-with-value.qml" "data/promise-executor-resolve.qml" "data/promise-get-length.qml" "data/promise-executor-reject.qml" "data/promise-reject-catch.qml" "data/promise-async-resolve-with-value.qml" "data/promise-async-reject-with-value.qml" "data/promise-all-resolve.qml" "data/promise-all-empty-input.qml" "data/promise-resolve-with-array.qml" "data/promise-all-reject-reject-is-last.qml" "data/promise-all-reject-reject-is-mid.qml" "data/promise-race-resolve-1st.qml" "data/promise-race-empty-input.qml" "data/promise-race-resolve-2nd.qml" "data/promise-race-resolve-1st-in-executor-function.qml" "data/promise-resolve-is-a-function.qml" "data/promise-resolve-function-length.qml" "data/promise-all-invoke-then-method.qml" "data/promise-resolve-with-empty.qml" "data/promise-executor-throw-exception.qml" "data/promise-executor-function-extensible.qml" "data/promise-all-noniterable-input.qml"
# OTHER_FILES = <EMPTY>
diff --git a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml
index 4ae9d3f2cf..63d33cbea6 100644
--- a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml
+++ b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in the
-// C++ code belonging to the test.
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQuick 2.0
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
index 8e4849e2d6..e97ab543d1 100644
--- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
+++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
@@ -637,7 +637,10 @@ void tst_qqmlqt::openUrlExternally()
const QUrl htmlTestFile = testFileUrl("test.html");
QDesktopServices::setUrlHandler("test", &handler, "noteCall");
QDesktopServices::setUrlHandler(htmlTestFile.scheme(), &handler, "noteCall");
-
+ const auto unset = qScopeGuard([&] {
+ QDesktopServices::unsetUrlHandler(htmlTestFile.scheme());
+ QDesktopServices::unsetUrlHandler("test");
+ });
QQmlComponent component(&engine, testFileUrl("openUrlExternally.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
@@ -648,9 +651,6 @@ void tst_qqmlqt::openUrlExternally()
QCOMPARE(handler.called,2);
QCOMPARE(handler.last, htmlTestFile);
-
- QDesktopServices::unsetUrlHandler("test");
- QDesktopServices::unsetUrlHandler(htmlTestFile.scheme());
}
void tst_qqmlqt::openUrlExternally_pragmaLibrary()
@@ -660,6 +660,10 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary()
const QUrl htmlTestFile = testFileUrl("test.html");
QDesktopServices::setUrlHandler("test", &handler, "noteCall");
QDesktopServices::setUrlHandler(htmlTestFile.scheme(), &handler, "noteCall");
+ const auto unset = qScopeGuard([&] {
+ QDesktopServices::unsetUrlHandler(htmlTestFile.scheme());
+ QDesktopServices::unsetUrlHandler("test");
+ });
QQmlComponent component(&engine, testFileUrl("openUrlExternally_lib.qml"));
QScopedPointer<QObject> object(component.create());
@@ -671,9 +675,6 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary()
QCOMPARE(handler.called,2);
QCOMPARE(handler.last, htmlTestFile);
-
- QDesktopServices::unsetUrlHandler("test");
- QDesktopServices::unsetUrlHandler(htmlTestFile.scheme());
}
void tst_qqmlqt::md5()
diff --git a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml
index 4ae9d3f2cf..63d33cbea6 100644
--- a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml
+++ b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml
@@ -1,8 +1,7 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in the
-// C++ code belonging to the test.
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQuick 2.0
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qqmltimer/dummy_imports.qml b/tests/auto/qml/qqmltimer/dummy_imports.qml
index f78e04d489..e189da6689 100644
--- a/tests/auto/qml/qqmltimer/dummy_imports.qml
+++ b/tests/auto/qml/qqmltimer/dummy_imports.qml
@@ -1,9 +1,8 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in the
-// C++ code belonging to the test.
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQml 2.0
-import QtQuick 2.0
+import QtQml
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qqmltypeloader/dummy_imports.qml b/tests/auto/qml/qqmltypeloader/dummy_imports.qml
index a4684b2007..e189da6689 100644
--- a/tests/auto/qml/qqmltypeloader/dummy_imports.qml
+++ b/tests/auto/qml/qqmltypeloader/dummy_imports.qml
@@ -1,9 +1,8 @@
// This file exists for the sole purpose for qmlimportscanner to find
// which modules it needs to extract for deployment.
-// Otherwise, it fails to find the imports that are expressed in the
-// C++ code belonging to the test.
+// Otherwise, it fails to find the imports that are expressed in C++.
-import QtQml 2.0
-import QtQuick 2.6
+import QtQml
+import QtQuick
-QtObject { } // This is needed in order to keep importscanner happy
+QtObject { }
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index a030e56b20..2463af827a 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -42,6 +42,7 @@
#include <QtQml/private/qqmlirloader_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QQmlComponent>
class tst_QQMLTypeLoader : public QQmlDataTest
{
diff --git a/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml b/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml
new file mode 100644
index 0000000000..f56fe3f422
--- /dev/null
+++ b/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml
@@ -0,0 +1,21 @@
+import QtQuick
+
+Item {
+ property string amText: InputMethod.locale.amText
+ property string decimalPoint: InputMethod.locale.decimalPoint
+ property string exponential: InputMethod.locale.exponential
+ property int firstDayOfWeek: InputMethod.locale.firstDayOfWeek
+ property string groupSeparator: InputMethod.locale.groupSeparator
+ property int measurementSystem: InputMethod.locale.measurementSystem
+ property string name: InputMethod.locale.name
+ property string nativeCountryName: InputMethod.locale.nativeCountryName
+ property string nativeLanguageName: InputMethod.locale.nativeLanguageName
+ property string negativeSign: InputMethod.locale.negativeSign
+ property string percent: InputMethod.locale.percent
+ property string pmText: InputMethod.locale.pmText
+ property string positiveSign: InputMethod.locale.positiveSign
+ property int textDirection: InputMethod.locale.textDirection
+ property var uiLanguages: InputMethod.locale.uiLanguages
+ property var weekDays: InputMethod.locale.weekDays
+ property string zeroDigit: InputMethod.locale.zeroDigit
+}
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 38e1e585ce..a61a632d24 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -295,8 +295,9 @@ void tst_qqmlvaluetypes::sizef()
void tst_qqmlvaluetypes::locale()
{
- {
- QQmlComponent component(&engine, testFileUrl("locale_read.qml"));
+ for (const QUrl &testFile :
+ { testFileUrl("locale_read.qml"), testFileUrl("locale_read_singleton.qml") }) {
+ QQmlComponent component(&engine, testFile);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
diff --git a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
index 19450aaef7..ea244fcbe4 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
+++ b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
@@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmlxmlhttprequest
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlxmlhttprequest)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
index 3353d8d5e1..d1dcf3829a 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
+++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
@@ -1093,6 +1093,10 @@ static const QString testString = QStringLiteral("Test-String");
void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component, QTemporaryFile &writeFile)> verifyFunction)
{
+#if defined(Q_OS_INTEGRITY)
+ QSKIP("There's no mounted filesystem on INTEGRITY.");
+#endif
+
// Create test files
QTemporaryFile writeFile;
QTemporaryFile readFile;
diff --git a/tests/auto/qml/qtqmlmodules/CMakeLists.txt b/tests/auto/qml/qtqmlmodules/CMakeLists.txt
index 2bff111bc7..6ff0cc01ae 100644
--- a/tests/auto/qml/qtqmlmodules/CMakeLists.txt
+++ b/tests/auto/qml/qtqmlmodules/CMakeLists.txt
@@ -22,6 +22,10 @@ qt_internal_add_test(tst_qtqmlmodules
TESTDATA ${test_data}
)
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qtqmlmodules)
+endif()
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
index e51cca9be4..d3cf585bb9 100644
--- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
+++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
@@ -30,6 +30,7 @@
#include <qtest.h>
#include <QQmlEngine>
#include <private/qv4identifiertable_p.h>
+#include <private/qv4engine_p.h>
class tst_qv4identifiertable : public QObject
{
diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
index f7e708e7aa..163aaa66f7 100644
--- a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
+++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
@@ -40,8 +40,7 @@ Item {
when: imageOnDisk.ready && imageOnDiskSmall.ready
function test_endresult_disk() {
- if ((Qt.platform.pluginName === "offscreen")
- || (Qt.platform.pluginName === "minimal"))
+ if (Qt.platform.pluginName === "minimal")
skip("grabImage does not work on offscreen/minimal platforms");
var image = grabImage(root);
@@ -77,8 +76,7 @@ Item {
}
function test_endresult_cache(data) {
- if ((Qt.platform.pluginName === "offscreen")
- || (Qt.platform.pluginName === "minimal"))
+ if (Qt.platform.pluginName === "minimal")
skip("grabImage does not work on offscreen/minimal platforms");
imageInCache.cache = data.cache;
diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt
index de701f909d..03b9829b68 100644
--- a/tests/auto/quick/CMakeLists.txt
+++ b/tests/auto/quick/CMakeLists.txt
@@ -80,6 +80,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qquickshadereffect)
add_subdirectory(qquickanimatedsprite)
add_subdirectory(qquickspritesequence)
+ add_subdirectory(qquickitemrhiintegration)
if(QT_FEATURE_opengl)
add_subdirectory(qquickframebufferobject)
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index b26a055e60..99e429c369 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -38,6 +38,7 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlproperty.h>
+#include <QQmlComponent>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
index 6296eb4da8..2120c0041d 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
@@ -37,6 +37,7 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QQmlComponent>
Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
index 136703d517..8f2ba0021c 100644
--- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
+++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
@@ -48,6 +48,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QQmlComponent>
+
#define EXPECT(cond) \
do { \
if (!errorAt && !(cond)) { \
@@ -147,17 +149,17 @@ void tst_QQuickAccessible::commonTests()
qDebug() << "testing" << accessibleRoleFileName;
- QQuickView *view = new QQuickView();
+ auto view = std::make_unique<QQuickView>();
// view->setFixedSize(240,320);
view->setSource(testFileUrl(accessibleRoleFileName));
view->show();
// view->setFocus();
QVERIFY(view->rootObject() != nullptr);
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view);
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view.get());
QVERIFY(iface);
- delete view;
+ view.reset();
QTestAccessibility::clearEvents();
}
@@ -168,12 +170,11 @@ void tst_QQuickAccessible::quickAttachedProperties()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nItem {\n"
"}", QUrl());
- QObject *object = component.create();
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
- QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object);
+ QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get());
QCOMPARE(attachedObject, static_cast<QObject*>(nullptr));
- delete object;
}
// Attaching to non-item
@@ -202,11 +203,11 @@ void tst_QQuickAccessible::quickAttachedProperties()
component.setData("import QtQuick 2.0\nItem {\n"
"Accessible.role: Accessible.Button\n"
"}", QUrl());
- QObject *object = component.create();
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
- QQuickAccessibleAttached::attachedProperties(object));
+ QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
@@ -220,7 +221,6 @@ void tst_QQuickAccessible::quickAttachedProperties()
QVERIFY2(p.value<QString>().isEmpty(), QTest::toString(p));
QCOMPARE(attachedObject->wasNameExplicitlySet(), false);
}
- delete object;
}
// Attached property
@@ -232,11 +232,11 @@ void tst_QQuickAccessible::quickAttachedProperties()
"Accessible.name: \"Donald\"\n"
"Accessible.description: \"Duck\"\n"
"}", QUrl());
- QObject *object = component.create();
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
- QQuickAccessibleAttached::attachedProperties(object));
+ QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
@@ -250,7 +250,6 @@ void tst_QQuickAccessible::quickAttachedProperties()
QCOMPARE(p.toString(), QLatin1String("Duck"));
QCOMPARE(attachedObject->wasNameExplicitlySet(), true);
}
- delete object;
}
// Check overriding of attached role for Text
@@ -262,10 +261,10 @@ void tst_QQuickAccessible::quickAttachedProperties()
"Accessible.name: \"TextButton\"\n"
"Accessible.description: \"Text Button\"\n"
"}", QUrl());
- QObject *object = component.create();
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
- QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object);
+ QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get());
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
@@ -278,7 +277,6 @@ void tst_QQuickAccessible::quickAttachedProperties()
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Text Button"));
}
- delete object;
}
// Check overriding of attached role for Text
{
@@ -294,10 +292,10 @@ void tst_QQuickAccessible::quickAttachedProperties()
"Accessible.description: \"Text Button\"\n"
"}\n"
"}", QUrl());
- QObject *object = component.create();
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
- QQuickListView *listview = qobject_cast<QQuickListView *>(object);
+ QQuickListView *listview = qobject_cast<QQuickListView *>(object.get());
QVERIFY(listview != nullptr);
QQuickItem *contentItem = listview->contentItem();
QQuickText *childItem = QQuickVisualTestUtils::findItem<QQuickText>(contentItem, "acc_text");
@@ -316,7 +314,6 @@ void tst_QQuickAccessible::quickAttachedProperties()
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Text Button"));
}
- delete object;
}
// Check that a name can be implicitly set.
{
@@ -328,11 +325,11 @@ void tst_QQuickAccessible::quickAttachedProperties()
Accessible.role: Accessible.Button
Accessible.description: "Text Button"
})", QUrl());
- QScopedPointer<QObject> object(component.create());
+ auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
- QQuickAccessibleAttached::attachedProperties(object.data()));
+ QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
QVERIFY(!attachedObject->wasNameExplicitlySet());
@@ -353,12 +350,12 @@ void tst_QQuickAccessible::basicPropertiesTest()
QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
QCOMPARE(app->childCount(), 0);
- QQuickView *window = new QQuickView();
+ auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("text.qml"));
window->show();
QCOMPARE(app->childCount(), 1);
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window);
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QCOMPARE(iface->childCount(), 1);
@@ -392,6 +389,7 @@ void tst_QQuickAccessible::basicPropertiesTest()
QCOMPARE(item->indexOfChild(text2), 1);
QVERIFY(!text2->state().editable);
QVERIFY(text2->state().readOnly);
+ QVERIFY(text2->state().focusable);
QCOMPARE(iface->indexOfChild(text2), -1);
QCOMPARE(text2->indexOfChild(item), -1);
@@ -483,7 +481,7 @@ void tst_QQuickAccessible::basicPropertiesTest()
attached->setRole(QAccessible::StaticText);
QVERIFY(!text3->state().readOnly);
- delete window;
+ window.reset();
QTestAccessibility::clearEvents();
}
@@ -502,14 +500,15 @@ QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y)
void tst_QQuickAccessible::hitTest()
{
- QQuickView *window = new QQuickView;
+ auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("hittest.qml"));
window->show();
- QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window);
+ QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(windowIface);
QAccessibleInterface *rootItem = windowIface->child(0);
- QRect rootRect = rootItem->rect();
+ // on Android the main window is always shown fullscreen
+ QRect rootRect = QRect(window->x(), window->y(), window->width(), window->height());
// check the root item from app
QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp);
@@ -555,13 +554,13 @@ void tst_QQuickAccessible::hitTest()
}
}
- delete window;
+ window.reset();
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::checkableTest()
{
- QScopedPointer<QQuickView> window(new QQuickView());
+ auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("checkbuttons.qml"));
window->show();
@@ -574,7 +573,7 @@ void tst_QQuickAccessible::checkableTest()
QAccessible::State activatedChange;
activatedChange.active = true;
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data());
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QAccessibleInterface *root = iface->child(0);
@@ -638,7 +637,7 @@ void tst_QQuickAccessible::checkableTest()
void tst_QQuickAccessible::ignoredTest()
{
- QScopedPointer<QQuickView> window(new QQuickView());
+ auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("ignored.qml"));
window->show();
@@ -651,7 +650,7 @@ void tst_QQuickAccessible::ignoredTest()
QAccessible::State activatedChange;
activatedChange.active = true;
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data());
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QAccessibleInterface *rectangleA = iface->child(0);
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index e2bac5ee39..2820762114 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -1863,9 +1863,8 @@ void tst_qquickanimations::fastFlickingBug()
void tst_qquickanimations::opacityAnimationFromZero()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
// not easy to verify this in threaded render loop
// since it's difficult to capture the first frame when scene graph
diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
index f1cfa05d38..72bb774a74 100644
--- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
+++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
@@ -39,6 +39,7 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QQmlComponent>
using namespace QQuickViewTestUtils;
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index 133583e72c..020097b9ef 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -37,6 +37,7 @@
#include <qpa/qplatformintegration.h>
#include <private/qguiapplication_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QFont>
class tst_qquickapplication : public QQmlDataTest
{
diff --git a/tests/auto/quick/qquickbehaviors/bindable.h b/tests/auto/quick/qquickbehaviors/bindable.h
index ebdf8711a3..9bc01c4568 100644
--- a/tests/auto/quick/qquickbehaviors/bindable.h
+++ b/tests/auto/quick/qquickbehaviors/bindable.h
@@ -31,6 +31,8 @@
#include <QObject>
#include <QQuickItem>
#include <qqmlregistration.h>
+#include <QBindable>
+#include <qproperty.h>
class TestBindable : public QQuickItem
{
diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
index e963082a1a..7ce44ee9dc 100644
--- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
+++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
@@ -591,9 +591,8 @@ void tst_qquickborderimage::progressAndStatusChanges()
#if QT_CONFIG(opengl)
void tst_qquickborderimage::borderImageMesh()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QQuickView *window = new QQuickView;
@@ -625,9 +624,8 @@ void tst_qquickborderimage::multiFrame_data()
void tst_qquickborderimage::multiFrame()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QFETCH(QString, qmlfile);
QFETCH(bool, asynchronous);
diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
index 3e9069d522..348948f5f6 100644
--- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
+++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
@@ -33,6 +33,7 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
+#include <QQmlComponent>
template <typename T> static T evaluate(QObject *scope, const QString &expression)
{
diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
index de7e3f1629..b7a7010a57 100644
--- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
+++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
@@ -31,6 +31,7 @@
#include <QtGui/qstylehints.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
+#include <QtQml/QQmlComponent>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 315b6d4fca..2e790dad08 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1663,7 +1663,8 @@ void tst_qquickflickable::cancelOnMouseGrab()
QQuickItem *item = window->rootObject()->findChild<QQuickItem*>("row");
auto mouse = QPointingDevice::primaryPointingDevice();
auto mousePriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(mouse));
- QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse);
+ QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), QPoint(130, 100),
+ Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse);
mousePriv->setExclusiveGrabber(&fakeMouseEv, fakeMouseEv.points().first(), item);
QTRY_COMPARE(flickable->contentX(), 0.);
diff --git a/tests/auto/quick/qquickframebufferobject/BLACKLIST b/tests/auto/quick/qquickframebufferobject/BLACKLIST
deleted file mode 100644
index de1bb7ad6d..0000000000
--- a/tests/auto/quick/qquickframebufferobject/BLACKLIST
+++ /dev/null
@@ -1,8 +0,0 @@
-[testInvalidate)
-# QTBUG-65614
-b2qt
-qnx
-
-[testThatStuffWorks]
-b2qt
-qnx
diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
index 19eeb5c60d..b69c3f7b96 100644
--- a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
+++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
+#include <QGuiApplication>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include <qopenglcontext.h>
@@ -209,6 +210,10 @@ void tst_QQuickFramebufferObject::testThatStuffWorks()
view.requestActivate();
QVERIFY(QTest::qWaitForWindowExposed(&view));
+ if (QGuiApplication::platformName() == "offscreen" &&
+ view.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("offscreen software rendering doesn't work with FBOs");
+
QImage result = view.grabWindow();
QCOMPARE(frameInfo.renderCount, 1);
@@ -249,6 +254,10 @@ void tst_QQuickFramebufferObject::testInvalidate()
view.requestActivate();
QVERIFY(QTest::qWaitForWindowExposed(&view));
+ if (QGuiApplication::platformName() == "offscreen" &&
+ view.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("offscreen software rendering doesn't work with FBOs");
+
QCOMPARE(frameInfo.fboSize, QSize(200, 200));
frameInfo.createFBOCount = 0;
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index 36c4b60718..37b3100e78 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -340,9 +340,8 @@ void tst_qquickimage::smooth()
void tst_qquickimage::mirror()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QMap<QQuickImage::FillMode, QImage> screenshots;
QList<QQuickImage::FillMode> fillModes;
@@ -555,9 +554,8 @@ void tst_qquickimage::big()
void tst_qquickimage::tiling_QTBUG_6716()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QFETCH(QString, source);
@@ -935,8 +933,7 @@ void tst_qquickimage::sourceClipRect()
QCOMPARE(image->implicitWidth(), sourceClipRect.isNull() ? 300 : sourceClipRect.width());
QCOMPARE(image->implicitHeight(), sourceClipRect.isNull() ? 300 : sourceClipRect.height());
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
QImage contents = toUnscaledImage(window->grabWindow());
if (contents.width() < sourceClipRect.width())
@@ -1164,9 +1161,8 @@ void tst_qquickimage::highDpiFillModesAndSizes()
void tst_qquickimage::hugeImages()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QQuickView view;
view.setSource(testFileUrl("hugeImages.qml"));
@@ -1223,9 +1219,8 @@ void tst_qquickimage::multiFrame_data()
void tst_qquickimage::multiFrame()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QFETCH(QString, qmlfile);
QFETCH(bool, asynchronous);
diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
index 1a479f7c0c..e0e2c9daac 100644
--- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
+++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
@@ -34,6 +34,7 @@
#include <QWaitCondition>
#include <QThreadPool>
#include <private/qqmlengine_p.h>
+#include <QQmlComponent>
Q_DECLARE_METATYPE(QQuickImageProvider*);
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index e4fe6df888..490933b06f 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
+#include <QtQml/QQmlComponent>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/qquickview.h>
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index 91ec08e854..623eb26dda 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -38,6 +38,7 @@
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtQuick/private/qquickanchors_p.h>
#include <QtGui/qstylehints.h>
#include <private/qquickitem_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -3675,9 +3676,8 @@ void tst_QQuickItem::childAt()
void tst_QQuickItem::grab()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabToImage not functional on minimal platforms");
QQuickView view;
view.setSource(testFileUrl("grabToImage.qml"));
diff --git a/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt b/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt
new file mode 100644
index 0000000000..0076d7f82b
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt
@@ -0,0 +1,36 @@
+qt_internal_add_test(tst_qquickitemrhiintegration
+ SOURCES
+ tst_qquickitemrhiintegration.cpp
+ rhiitem.cpp rhiitem.h rhiitem_p.h
+ testrhiitem.cpp testrhiitem.h
+ PUBLIC_LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+)
+
+qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\":/data\\\"
+)
+
+qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
+)
+
+qt_internal_add_shaders(tst_qquickitemrhiintegration "shaders"
+ PREFIX
+ "/"
+ FILES
+ "texture.vert"
+ "texture.frag"
+)
+
+qt_add_qml_module(tst_qquickitemrhiintegration
+ URI Test
+ VERSION 1.0
+)
diff --git a/tools/qmltc/prototype/visitor.cpp b/tests/auto/quick/qquickitemrhiintegration/data/test.qml
index 307cb992ea..1262936761 100644
--- a/tools/qmltc/prototype/visitor.cpp
+++ b/tests/auto/quick/qquickitemrhiintegration/data/test.qml
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the tools applications of the Qt Toolkit.
+** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
@@ -26,26 +26,38 @@
**
****************************************************************************/
-#include "prototype/visitor.h"
+import QtQuick
+import Test
-#include <QtCore/qdir.h>
-#include <QtCore/qfileinfo.h>
+Item {
+ width: 640
+ height: 480
-namespace Qmltc {
-Visitor::Visitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
- const QString &implicitImportDirectory, const QStringList &qmltypesFiles)
- : QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmltypesFiles)
-{
-}
+ Text {
+ id: apiInfo
+ color: "black"
+ font.pixelSize: 16
+ property int api: GraphicsInfo.api
+ text: {
+ if (GraphicsInfo.api === GraphicsInfo.OpenGLRhi)
+ "OpenGL on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D11Rhi)
+ "D3D11 on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.VulkanRhi)
+ "Vulkan on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.MetalRhi)
+ "Metal on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Null)
+ "Null on QRhi";
+ else
+ "Unknown API";
+ }
+ }
-bool Visitor::visit(QQmlJS::AST::UiInlineComponent *component)
-{
- if (!QQmlJSImportVisitor::visit(component))
- return false;
- m_logger->logCritical(u"Inline components are not supported"_qs, Log_Compiler,
- component->firstSourceLocation());
- // despite the failure, return true here so that we do not assert in
- // QQmlJSImportVisitor::endVisit(UiInlineComponent)
- return true;
-}
+ TestRhiItem {
+ anchors.centerIn: parent
+ width: 400
+ height: 400
+ color: "red"
+ }
}
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp
new file mode 100644
index 0000000000..5bfd3a850d
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#include "rhiitem_p.h"
+
+RhiItemNode::RhiItemNode(RhiItem *item)
+ : m_item(item)
+{
+ m_window = m_item->window();
+ Q_ASSERT(m_window);
+ connect(m_window, &QQuickWindow::beforeRendering, this, &RhiItemNode::render);
+ connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
+ if (m_window->effectiveDevicePixelRatio() != m_dpr)
+ m_item->update();
+ });
+}
+
+RhiItemNode::~RhiItemNode()
+{
+ delete m_renderer;
+ delete m_sgWrapperTexture;
+ releaseNativeTexture();
+}
+
+QSGTexture *RhiItemNode::texture() const
+{
+ return m_sgWrapperTexture;
+}
+
+void RhiItemNode::createNativeTexture()
+{
+ Q_ASSERT(!m_texture);
+ Q_ASSERT(!m_pixelSize.isEmpty());
+
+ m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, m_pixelSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!m_texture->create()) {
+ qWarning("Failed to create RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
+ delete m_texture;
+ m_texture = nullptr;
+ }
+}
+
+void RhiItemNode::releaseNativeTexture()
+{
+ if (m_texture) {
+ m_texture->deleteLater();
+ m_texture = nullptr;
+ }
+}
+
+void RhiItemNode::sync()
+{
+ if (!m_rhi) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource));
+ if (!m_rhi) {
+ qWarning("No QRhi found for window %p, RhiItem will not be functional", m_window);
+ return;
+ }
+ }
+
+ QSize newSize(m_item->explicitTextureWidth(), m_item->explicitTextureHeight());
+ if (newSize.isEmpty()) {
+ m_dpr = m_window->effectiveDevicePixelRatio();
+ const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin);
+ newSize = QSize(qMax<int>(minTexSize, m_item->width()),
+ qMax<int>(minTexSize, m_item->height())) * m_dpr;
+ }
+
+ bool needsNew = !m_sgWrapperTexture;
+ if (newSize != m_pixelSize) {
+ needsNew = true;
+ m_pixelSize = newSize;
+ }
+
+ if (needsNew) {
+ if (m_texture && m_sgWrapperTexture) {
+ m_texture->setPixelSize(m_pixelSize);
+ if (m_texture->create())
+ m_sgWrapperTexture->setTextureSize(m_pixelSize);
+ else
+ qWarning("Failed to recreate RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
+ } else {
+ delete m_sgWrapperTexture;
+ m_sgWrapperTexture = nullptr;
+ releaseNativeTexture();
+ createNativeTexture();
+ if (m_texture) {
+ m_sgWrapperTexture = new QSGPlainTexture;
+ m_sgWrapperTexture->setOwnsTexture(false);
+ m_sgWrapperTexture->setTexture(m_texture);
+ m_sgWrapperTexture->setTextureSize(m_pixelSize);
+ m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending());
+ setTexture(m_sgWrapperTexture);
+ }
+ }
+ RhiItemPrivate::get(m_item)->effectiveTextureSize = m_pixelSize;
+ emit m_item->effectiveTextureSizeChanged();
+ if (m_texture)
+ m_renderer->initialize(m_rhi, m_texture);
+ }
+
+ if (m_sgWrapperTexture && m_sgWrapperTexture->hasAlphaChannel() != m_item->alphaBlending()) {
+ m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending());
+ // hasAlphaChannel is mapped to QSGMaterial::Blending in setTexture() so that has to be called again
+ setTexture(m_sgWrapperTexture);
+ }
+
+ m_renderer->synchronize(m_item);
+}
+
+void RhiItemNode::render()
+{
+ // called before Qt Quick starts recording its main render pass
+
+ if (!m_rhi || !m_texture || !m_renderer)
+ return;
+
+ if (!m_renderPending)
+ return;
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ QRhiCommandBuffer *cb = nullptr;
+ QRhiSwapChain *swapchain = static_cast<QRhiSwapChain *>(
+ rif->getResource(m_window, QSGRendererInterface::RhiSwapchainResource));
+ cb = swapchain ? swapchain->currentFrameCommandBuffer()
+ : static_cast<QRhiCommandBuffer *>(rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
+ if (!cb) {
+ qWarning("Neither swapchain nor redirected command buffer are available.");
+ return;
+ }
+
+ m_renderPending = false;
+ m_renderer->render(cb);
+
+ markDirty(QSGNode::DirtyMaterial);
+ emit textureChanged();
+}
+
+void RhiItemNode::scheduleUpdate()
+{
+ m_renderPending = true;
+ m_window->update(); // ensure getting to beforeRendering() at some point
+}
+
+void RhiItemNode::setMirrorVertically(bool b)
+{
+ if (m_rhi->isYUpInFramebuffer())
+ b = !b;
+
+ setTextureCoordinatesTransform(b ? QSGSimpleTextureNode::MirrorVertically : QSGSimpleTextureNode::NoTransform);
+}
+
+RhiItem::RhiItem(QQuickItem *parent)
+ : QQuickItem(*new RhiItemPrivate, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QSGNode *RhiItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ Q_D(RhiItem);
+ RhiItemNode *n = static_cast<RhiItemNode *>(node);
+
+ // Changing to an empty size should not involve destroying and then later
+ // recreating the node, because we do not know how expensive the user's
+ // renderer setup is. Rather, keep the node if it already exist, and clamp
+ // all accesses to width and height. Hence the unusual !n condition here.
+ if (!n && (width() <= 0 || height() <= 0))
+ return nullptr;
+
+ if (!n) {
+ if (!d->node)
+ d->node = new RhiItemNode(this);
+ n = d->node;
+ }
+
+ if (!n->hasRenderer()) {
+ RhiItemRenderer *r = createRenderer();
+ if (r) {
+ r->data = n;
+ n->setRenderer(r);
+ } else {
+ qWarning("No RhiItemRenderer was created; the item will not render");
+ delete n;
+ d->node = nullptr;
+ return nullptr;
+ }
+ }
+
+ n->sync();
+
+ if (!n->isValid()) {
+ delete n;
+ d->node = nullptr;
+ return nullptr;
+ }
+
+ n->setMirrorVertically(d->mirrorVertically);
+ n->setFiltering(QSGTexture::Linear);
+ n->setRect(0, 0, qMax<int>(0, width()), qMax<int>(0, height()));
+
+ n->scheduleUpdate();
+
+ return n;
+}
+
+void RhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+ if (newGeometry.size() != oldGeometry.size())
+ update();
+}
+
+void RhiItem::releaseResources()
+{
+ // called on the gui thread if the item is removed from scene
+
+ Q_D(RhiItem);
+ d->node = nullptr;
+}
+
+void RhiItem::invalidateSceneGraph()
+{
+ // called on the render thread when the scenegraph is invalidated
+
+ Q_D(RhiItem);
+ d->node = nullptr;
+}
+
+bool RhiItem::isTextureProvider() const
+{
+ return true;
+}
+
+QSGTextureProvider *RhiItem::textureProvider() const
+{
+ if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true
+ return QQuickItem::textureProvider();
+
+ QQuickWindow *w = window();
+ if (!w || !w->isSceneGraphInitialized() || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
+ qWarning("RhiItem::textureProvider: can only be queried on the rendering thread of an exposed window");
+ return nullptr;
+ }
+
+ Q_D(const RhiItem);
+ if (!d->node) // create a node to have a provider, the texture will be null but that's ok
+ d->node = new RhiItemNode(const_cast<RhiItem *>(this));
+
+ return d->node;
+}
+
+int RhiItem::explicitTextureWidth() const
+{
+ Q_D(const RhiItem);
+ return d->explicitTextureWidth;
+}
+
+void RhiItem::setExplicitTextureWidth(int w)
+{
+ Q_D(RhiItem);
+ if (d->explicitTextureWidth == w)
+ return;
+
+ d->explicitTextureWidth = w;
+ emit explicitTextureWidthChanged();
+ update();
+}
+
+int RhiItem::explicitTextureHeight() const
+{
+ Q_D(const RhiItem);
+ return d->explicitTextureHeight;
+}
+
+void RhiItem::setExplicitTextureHeight(int h)
+{
+ Q_D(RhiItem);
+ if (d->explicitTextureHeight == h)
+ return;
+
+ d->explicitTextureHeight = h;
+ emit explicitTextureHeightChanged();
+ update();
+}
+
+QSize RhiItem::effectiveTextureSize() const
+{
+ Q_D(const RhiItem);
+ return d->effectiveTextureSize;
+}
+
+bool RhiItem::alphaBlending() const
+{
+ Q_D(const RhiItem);
+ return d->blend;
+}
+
+void RhiItem::setAlphaBlending(bool enable)
+{
+ Q_D(RhiItem);
+ if (d->blend == enable)
+ return;
+
+ d->blend = enable;
+ emit alphaBlendingChanged();
+ update();
+}
+
+bool RhiItem::mirrorVertically() const
+{
+ Q_D(const RhiItem);
+ return d->mirrorVertically;
+}
+
+void RhiItem::setMirrorVertically(bool enable)
+{
+ Q_D(RhiItem);
+ if (d->mirrorVertically == enable)
+ return;
+
+ d->mirrorVertically = enable;
+ emit mirrorVerticallyChanged();
+ update();
+}
+
+void RhiItemRenderer::update()
+{
+ if (data)
+ static_cast<RhiItemNode *>(data)->scheduleUpdate();
+}
+
+RhiItemRenderer::~RhiItemRenderer()
+{
+}
+
+void RhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(outputTexture);
+}
+
+void RhiItemRenderer::synchronize(RhiItem *item)
+{
+ Q_UNUSED(item);
+}
+
+void RhiItemRenderer::render(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem.h
new file mode 100644
index 0000000000..32de7922a0
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#ifndef RHIITEM_H
+#define RHIITEM_H
+
+#include <QQuickItem>
+#include <QtGui/private/qrhi_p.h>
+
+class RhiItem;
+class RhiItemPrivate;
+
+class RhiItemRenderer
+{
+public:
+ virtual ~RhiItemRenderer();
+ virtual void initialize(QRhi *rhi, QRhiTexture *outputTexture);
+ virtual void synchronize(RhiItem *item);
+ virtual void render(QRhiCommandBuffer *cb);
+
+ void update();
+
+private:
+ void *data;
+ friend class RhiItem;
+};
+
+class RhiItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(RhiItem)
+
+ Q_PROPERTY(int explicitTextureWidth READ explicitTextureWidth WRITE setExplicitTextureWidth NOTIFY explicitTextureWidthChanged)
+ Q_PROPERTY(int explicitTextureHeight READ explicitTextureHeight WRITE setExplicitTextureHeight NOTIFY explicitTextureHeightChanged)
+ Q_PROPERTY(QSize effectiveTextureSize READ effectiveTextureSize NOTIFY effectiveTextureSizeChanged)
+ Q_PROPERTY(bool alphaBlending READ alphaBlending WRITE setAlphaBlending NOTIFY alphaBlendingChanged)
+ Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged)
+
+public:
+ RhiItem(QQuickItem *parent = nullptr);
+
+ virtual RhiItemRenderer *createRenderer() = 0;
+
+ int explicitTextureWidth() const;
+ void setExplicitTextureWidth(int w);
+ int explicitTextureHeight() const;
+ void setExplicitTextureHeight(int h);
+
+ QSize effectiveTextureSize() const;
+
+ bool alphaBlending() const;
+ void setAlphaBlending(bool enable);
+
+ bool mirrorVertically() const;
+ void setMirrorVertically(bool enable);
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void releaseResources() override;
+ bool isTextureProvider() const override;
+ QSGTextureProvider *textureProvider() const override;
+
+Q_SIGNALS:
+ void explicitTextureWidthChanged();
+ void explicitTextureHeightChanged();
+ void effectiveTextureSizeChanged();
+ void alphaBlendingChanged();
+ void mirrorVerticallyChanged();
+
+private Q_SLOTS:
+ void invalidateSceneGraph();
+};
+
+#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h
new file mode 100644
index 0000000000..00ffe9cb59
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#ifndef RHIITEM_P_H
+#define RHIITEM_P_H
+
+#include "rhiitem.h"
+#include <QSGSimpleTextureNode>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
+
+class RhiItemNode : public QSGTextureProvider, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ RhiItemNode(RhiItem *item);
+ ~RhiItemNode();
+
+ QSGTexture *texture() const override;
+
+ void sync();
+ bool isValid() const { return m_rhi && m_texture && m_sgWrapperTexture; }
+ void scheduleUpdate();
+ bool hasRenderer() const { return m_renderer; }
+ void setRenderer(RhiItemRenderer *r) { m_renderer = r; }
+ void setMirrorVertically(bool b);
+
+private slots:
+ void render();
+
+private:
+ void createNativeTexture();
+ void releaseNativeTexture();
+
+ RhiItem *m_item;
+ QQuickWindow *m_window;
+ QSize m_pixelSize;
+ qreal m_dpr = 0.0f;
+ QRhi *m_rhi = nullptr;
+ QRhiTexture *m_texture = nullptr;
+ QSGPlainTexture *m_sgWrapperTexture = nullptr;
+ bool m_renderPending = true;
+ RhiItemRenderer *m_renderer = nullptr;
+};
+
+class RhiItemPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(RhiItem)
+public:
+ static RhiItemPrivate *get(RhiItem *item) { return item->d_func(); }
+ mutable RhiItemNode *node = nullptr;
+ int explicitTextureWidth = 0;
+ int explicitTextureHeight = 0;
+ bool blend = true;
+ bool mirrorVertically = false;
+ QSize effectiveTextureSize;
+};
+
+#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp
new file mode 100644
index 0000000000..c5a83f3f65
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#include "testrhiitem.h"
+#include <QFile>
+
+static const QSize TEX_SIZE(512, 512);
+
+void TestRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture)
+{
+ m_rhi = rhi;
+ m_output = outputTexture;
+
+ if (!m_ds) {
+ m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_output->pixelSize()));
+ m_ds->create();
+ } else if (m_ds->pixelSize() != m_output->pixelSize()) {
+ m_ds->setPixelSize(m_output->pixelSize());
+ m_ds->create();
+ }
+
+ if (!m_rt) {
+ m_rt.reset(m_rhi->newTextureRenderTarget({ { m_output }, m_ds.data() }));
+ m_rp.reset(m_rt->newCompatibleRenderPassDescriptor());
+ m_rt->setRenderPassDescriptor(m_rp.data());
+ m_rt->create();
+ }
+
+ if (!scene.vbuf)
+ initScene();
+
+ const QSize outputSize = m_output->pixelSize();
+ scene.mvp = m_rhi->clipSpaceCorrMatrix();
+ scene.mvp.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ scene.mvp.translate(0, 0, -4);
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 0, 64, scene.mvp.constData());
+}
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+
+ return QShader();
+}
+
+void TestRenderer::updateTexture()
+{
+ QImage img(TEX_SIZE, QImage::Format_RGBA8888);
+ img.fill(itemData.color);
+
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ scene.resourceUpdates->uploadTexture(scene.tex.data(), img);
+}
+
+void TestRenderer::initScene()
+{
+ static const float tri[] = {
+ 0.0f, 0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, 0.0f, 0.0f,
+ 0.5f, -0.5f, 1.0f, 0.0f
+ };
+
+ scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(tri)));
+ scene.vbuf->create();
+
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.data(), tri);
+
+ scene.ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
+ scene.ubuf->create();
+
+ const qint32 flip = m_rhi->isYUpInFramebuffer() ? 1 : 0;
+ scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 64, 4, &flip);
+
+ scene.tex.reset(m_rhi->newTexture(QRhiTexture::RGBA8, TEX_SIZE));
+ scene.tex->create();
+
+ scene.sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ scene.sampler->create();
+
+ scene.srb.reset(m_rhi->newShaderResourceBindings());
+ scene.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.ubuf.data()),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.tex.data(), scene.sampler.data())
+ });
+ scene.srb->create();
+
+ scene.ps.reset(m_rhi->newGraphicsPipeline());
+ scene.ps->setDepthTest(true);
+ scene.ps->setDepthWrite(true);
+ scene.ps->setDepthOp(QRhiGraphicsPipeline::Less);
+ scene.ps->setCullMode(QRhiGraphicsPipeline::Back);
+ scene.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
+ QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
+ Q_ASSERT(vs.isValid());
+ QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
+ Q_ASSERT(fs.isValid());
+ scene.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, vs },
+ { QRhiShaderStage::Fragment, fs }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 4 * sizeof(float) },
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
+ });
+ scene.ps->setVertexInputLayout(inputLayout);
+ scene.ps->setShaderResourceBindings(scene.srb.data());
+ scene.ps->setRenderPassDescriptor(m_rp.data());
+ scene.ps->create();
+}
+
+void TestRenderer::synchronize(RhiItem *rhiItem)
+{
+ TestRhiItem *item = static_cast<TestRhiItem *>(rhiItem);
+ if (item->color() != itemData.color) {
+ itemData.color = item->color();
+ updateTexture();
+ }
+}
+
+void TestRenderer::render(QRhiCommandBuffer *cb)
+{
+ QRhiResourceUpdateBatch *rub = scene.resourceUpdates;
+ if (rub)
+ scene.resourceUpdates = nullptr;
+
+ const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
+ cb->beginPass(m_rt.data(), clearColor, { 1.0f, 0 }, rub);
+
+ cb->setGraphicsPipeline(scene.ps.data());
+ const QSize outputSize = m_output->pixelSize();
+ cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { scene.vbuf.data(), 0 },
+ };
+ cb->setVertexInput(0, 1, vbufBindings);
+ cb->draw(3);
+
+ cb->endPass();
+}
+
+void TestRhiItem::setColor(QColor c)
+{
+ if (m_color == c)
+ return;
+
+ m_color = c;
+ emit colorChanged();
+ update();
+}
diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h
new file mode 100644
index 0000000000..735c8e5355
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#ifndef TESTRHIITEM_H
+#define TESTRHIITEM_H
+
+#include "rhiitem.h"
+
+class TestRenderer : public RhiItemRenderer
+{
+public:
+ void initialize(QRhi *rhi, QRhiTexture *outputTexture) override;
+ void synchronize(RhiItem *item) override;
+ void render(QRhiCommandBuffer *cb) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+ QRhiTexture *m_output = nullptr;
+ QScopedPointer<QRhiRenderBuffer> m_ds;
+ QScopedPointer<QRhiTextureRenderTarget> m_rt;
+ QScopedPointer<QRhiRenderPassDescriptor> m_rp;
+
+ struct {
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr;
+ QScopedPointer<QRhiBuffer> vbuf;
+ QScopedPointer<QRhiBuffer> ubuf;
+ QScopedPointer<QRhiShaderResourceBindings> srb;
+ QScopedPointer<QRhiGraphicsPipeline> ps;
+ QScopedPointer<QRhiSampler> sampler;
+ QScopedPointer<QRhiTexture> tex;
+ QMatrix4x4 mvp;
+ } scene;
+
+ struct {
+ QColor color;
+ } itemData;
+
+ void initScene();
+ void updateTexture();
+};
+
+class TestRhiItem : public RhiItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TestRhiItem)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+
+public:
+ RhiItemRenderer *createRenderer() override { return new TestRenderer; }
+
+ QColor color() const { return m_color; }
+ void setColor(QColor c);
+
+signals:
+ void colorChanged();
+
+private:
+ QColor m_color;
+};
+
+#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.frag b/tests/auto/quick/qquickitemrhiintegration/texture.frag
new file mode 100644
index 0000000000..a7b8bce0ad
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/texture.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+};
+
+layout(binding = 1) uniform sampler2D tex;
+
+void main()
+{
+ vec4 c = texture(tex, v_texcoord);
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.vert b/tests/auto/quick/qquickitemrhiintegration/texture.vert
new file mode 100644
index 0000000000..bf678d1e9b
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/texture.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+};
+
+void main()
+{
+ v_texcoord = texcoord;
+ if (flip != 0)
+ v_texcoord.y = 1.0 - v_texcoord.y;
+ gl_Position = mvp * position;
+}
diff --git a/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp b/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp
new file mode 100644
index 0000000000..0c8581d907
--- /dev/null
+++ b/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+#include <qtest.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtGui/private/qrhi_p.h>
+
+#include <QQuickView>
+#include <QSGRendererInterface>
+#include <private/qsgrhisupport_p.h>
+
+class tst_QQuickItemRhiIntegration : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_QQuickItemRhiIntegration();
+
+private slots:
+ void initTestCase() override;
+ void cleanupTestCase();
+ void rhiItem();
+};
+
+tst_QQuickItemRhiIntegration::tst_QQuickItemRhiIntegration()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+}
+
+void tst_QQuickItemRhiIntegration::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ qDebug() << "RHI backend:" << QSGRhiSupport::instance()->rhiBackendName();
+}
+
+void tst_QQuickItemRhiIntegration::cleanupTestCase()
+{
+}
+
+void tst_QQuickItemRhiIntegration::rhiItem()
+{
+ if (QGuiApplication::platformName() == QLatin1String("offscreen")
+ || QGuiApplication::platformName() == QLatin1String("minimal"))
+ {
+ QSKIP("Skipping on offscreen/minimal");
+ }
+
+ // Load up a scene that instantiates a TestRhiItem, which in turn
+ // renders a triangle with QRhi into a QRhiTexture and then draws
+ // a quad textured with it.
+
+ QQuickView view;
+ view.setSource(testFileUrl(QLatin1String("test.qml")));
+ view.setResizeMode(QQuickView::SizeViewToRootObject);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi()))
+ QSKIP("Scenegraph does not use QRhi, test is pointless");
+
+ QImage result = view.grabWindow();
+ QVERIFY(!result.isNull());
+
+ const int tolerance = 5;
+
+ // The result is a red triangle in the center of the view, confirm at least one pixel.
+ QRgb a = result.pixel(result.width() / 2, result.height() / 2);
+ QRgb b = QColor(Qt::red).rgb();
+ QVERIFY(qAbs(qRed(a) - qRed(b)) <= tolerance
+ && qAbs(qGreen(a) - qGreen(b)) <= tolerance
+ && qAbs(qBlue(a) - qBlue(b)) <= tolerance);
+}
+
+#include "tst_qquickitemrhiintegration.moc"
+
+QTEST_MAIN(tst_QQuickItemRhiIntegration)
diff --git a/tests/auto/quick/qquicklayouts/BLACKLIST b/tests/auto/quick/qquicklayouts/BLACKLIST
index a9e6a444af..5859d0f7cd 100644
--- a/tests/auto/quick/qquicklayouts/BLACKLIST
+++ b/tests/auto/quick/qquicklayouts/BLACKLIST
@@ -1,2 +1,11 @@
[Tests_GridLayout::test_spacings]
qnx
+#QTBUG-101327
+[tst_stacklayout::compile]
+qnx
+#QTBUG-101327
+[tst_gridlayout::compile]
+qnx
+#QTBUG-101327
+[tst_rowlayout::compile]
+qnx
diff --git a/tests/auto/quick/qquicklistview/data/qtbug48044.qml b/tests/auto/quick/qquicklistview/data/qtbug48044.qml
index d318643c1c..368b6bd4bd 100644
--- a/tests/auto/quick/qquicklistview/data/qtbug48044.qml
+++ b/tests/auto/quick/qquicklistview/data/qtbug48044.qml
@@ -116,7 +116,7 @@ Item {
color: header ? "yellow" : "cyan"
border.color: "black"
height: 50
- width: parent.width
+ width: listView.width
Text {
anchors.centerIn: parent
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 6975fa2dbd..d3ecdaae68 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -37,11 +37,13 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
+#include <QtQml/qqmlcomponent.h>
#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/private/qquicktransition_p.h>
#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
diff --git a/tests/auto/quick/qquickloader/data/noEngine.qml b/tests/auto/quick/qquickloader/data/noEngine.qml
new file mode 100644
index 0000000000..19e619f32e
--- /dev/null
+++ b/tests/auto/quick/qquickloader/data/noEngine.qml
@@ -0,0 +1,32 @@
+import QtQuick 2
+
+Item {
+ id: root
+ property bool a: false
+ property int changes: 0
+ onAChanged: {
+ m.model = 0
+ m.model = 1
+ ++changes;
+ }
+
+ Repeater {
+ id: m
+ model: 1
+
+ Item {
+ Timer {
+ onTriggered: {
+ root.a = true
+ l.source = "loaded.qml"
+ }
+ interval: 0
+ running: true
+ }
+
+ Loader {
+ id: l
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index cf1c4fcce0..e990e94bc3 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -134,6 +134,7 @@ private slots:
void setSourceAndCheckStatus();
void asyncLoaderRace();
+ void noEngine();
};
Q_DECLARE_METATYPE(QList<QQmlError>)
@@ -1533,6 +1534,20 @@ void tst_QQuickLoader::asyncLoaderRace()
QCOMPARE(loader->item(), nullptr);
}
+void tst_QQuickLoader::noEngine()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("noEngine.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+
+ const QString message = url.toString()
+ + QStringLiteral(":27:13: QML Loader: createComponent: Cannot find a QML engine.");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ QTRY_COMPARE(o->property("changes").toInt(), 1);
+}
+
QTEST_MAIN(tst_QQuickLoader)
#include "tst_qquickloader.moc"
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 0cbb047048..3d1f967b24 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -41,6 +41,8 @@
#include <QtGui/qstylehints.h>
#include <QtGui/QCursor>
#include <QtGui/QScreen>
+#include <QEvent>
+#include <QQmlComponent>
#include <qpa/qwindowsysteminterface.h>
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
@@ -741,7 +743,9 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnResize()
QCOMPARE(mouseRegion->mouseX(), 0.0);
QCOMPARE(mouseRegion->mouseY(), 0.0);
- QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), Qt::LeftButton, Qt::LeftButton, {});
+ QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(),
+ window.mapToGlobal(rect->position().toPoint()),
+ Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &event);
QVERIFY(!mouseRegion->property("emitPositionChanged").toBool());
@@ -854,8 +858,10 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
- QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
- QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &pressEvent);
@@ -872,7 +878,8 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
+ QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
@@ -918,16 +925,19 @@ void tst_QQuickMouseArea::doubleClick()
// The sequence for a double click is:
// press, release, (click), press, double click, release
- QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("released").toInt(), 1);
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
@@ -949,10 +959,12 @@ void tst_QQuickMouseArea::clickTwice()
QVERIFY(mouseArea);
mouseArea->setAcceptedButtons(acceptedButtons);
- QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
@@ -961,7 +973,8 @@ void tst_QQuickMouseArea::clickTwice()
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
@@ -984,16 +997,19 @@ void tst_QQuickMouseArea::invalidClick()
// The sequence for a double click is:
// press, release, (click), press, double click, release
- QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("released").toInt(), 0);
QGuiApplication::sendEvent(&window, &pressEvent);
- QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
+ QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
+ window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
@@ -1273,9 +1289,8 @@ void tst_QQuickMouseArea::hoverPropagation()
void tst_QQuickMouseArea::hoverVisible()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverVisible.qml")));
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 8f45368f1c..2a0e1e5b6d 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -44,6 +44,7 @@
#include <QtGui/qstandarditemmodel.h>
#include <QStringListModel>
#include <QFile>
+#include <QEvent>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
@@ -1539,7 +1540,8 @@ void tst_QQuickPathView::mouseDrag()
QTest::qWait(100);
{
- QMouseEvent mv(QEvent::MouseMove, QPoint(50,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QMouseEvent mv(QEvent::MouseMove, QPoint(50,100), window->mapToGlobal(QPoint(50,100)),
+ Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
QGuiApplication::sendEvent(window.data(), &mv);
}
// first move beyond threshold does not trigger drag
@@ -1553,7 +1555,8 @@ void tst_QQuickPathView::mouseDrag()
QCOMPARE(dragEndedSpy.count(), 0);
{
- QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), window->mapToGlobal(QPoint(90,100)),
+ Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
QGuiApplication::sendEvent(window.data(), &mv);
}
// next move beyond threshold does trigger drag
@@ -1933,7 +1936,8 @@ void tst_QQuickPathView::cancelDrag()
// steal mouse grab - cancels PathView dragging
auto mouse = QPointingDevice::primaryPointingDevice();
auto mousePriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(mouse));
- QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse);
+ QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), QPoint(130, 100),
+ Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse);
mousePriv->setExclusiveGrabber(&fakeMouseEv, fakeMouseEv.points().first(), nullptr);
// returns to a snap point.
diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
index e8a46e41ce..0cdbeaf17a 100644
--- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
+++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QtQuick/qquickview.h>
#include <qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickpositioners_p.h>
#include <QtQuick/private/qquicktransition_p.h>
diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
index e28bfffe6f..e07e97ddd8 100644
--- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
+++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
@@ -72,8 +72,7 @@ void tst_qquickrectangle::color()
QVERIFY(QTest::qWaitForWindowExposed(&view));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
QImage image = view.grabWindow();
diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
index 0b12aa551f..e8b53d046f 100644
--- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
+++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
@@ -30,6 +30,7 @@
#include <QtTest/QSignalSpy>
#include <QtQml/qqmlengine.h>
#include <QtQuick/qquickview.h>
+#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
diff --git a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
index a77839afa4..8d95a6781e 100644
--- a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
+++ b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
@@ -29,6 +29,7 @@
#include <qtest.h>
#include <QDebug>
#include <QQmlEngine>
+#include <QQmlComponent>
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index d72a3d265e..0d2debcf1f 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -282,9 +282,8 @@ void tst_QQuickShape::render()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -306,9 +305,8 @@ void tst_QQuickShape::renderWithMultipleSp()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -330,9 +328,8 @@ void tst_QQuickShape::radialGrad()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -354,9 +351,8 @@ void tst_QQuickShape::conicalGrad()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -378,9 +374,8 @@ void tst_QQuickShape::renderPolyline()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -407,9 +402,8 @@ void tst_QQuickShape::renderMultiline()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -473,9 +467,8 @@ void tst_QQuickShape::polylineDataTypes()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -624,9 +617,8 @@ void tst_QQuickShape::multilineDataTypes()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
@@ -672,9 +664,8 @@ void tst_QQuickShape::multilineStronglyTyped()
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
provider->setPaths(m_lowPolyLogo);
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
diff --git a/tests/auto/quick/qquickstates/data/anchorRewind.qml b/tests/auto/quick/qquickstates/data/anchorRewind.qml
new file mode 100644
index 0000000000..740c94cf42
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/anchorRewind.qml
@@ -0,0 +1,31 @@
+import QtQuick
+
+Item {
+ width: 400
+ height: 400
+ Item {
+ id: outer
+ anchors.fill: parent
+ Item {
+ id: inner
+ width: parent.width / 2
+ height: parent.height / 2
+ anchors.left: parent.right
+ anchors.top: parent.bottom
+ }
+ states: [
+ State {
+ when: true
+ AnchorChanges {
+ target: inner
+ anchors.left: outer.left
+ anchors.top: outer.top
+ }
+ }
+ ]
+ transitions: Transition {
+ AnchorAnimation {}
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qquickstates/data/noStateOsciallation.qml b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
new file mode 100644
index 0000000000..f0d7aeeb6d
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ property int number: 2
+ property int stateChangeCounter: 0
+
+ Item {
+ id: item
+ onStateChanged: ++stateChangeCounter
+ states: [
+ State {
+ name: "n1"
+ when: root.number === 1
+ },
+ State {
+ name: "n2"
+ when: root.number === 2
+ }
+ ]
+ }
+}
diff --git a/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml b/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml
index 9680e806b8..d3873883cd 100644
--- a/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml
+++ b/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml
@@ -3,25 +3,41 @@ import QtQuick
Item {
id: root
property alias childWidth: firstChild.width
+ property alias childX: firstChild.x
property alias childRotation: firstChild.rotation
property double myrotation: 100
property double myrotation2: 200
height: 400
+ y: 40
Item {
id: firstChild
height: parent.height
width: height
+ y: parent.y
+ x: y
rotation: root.myrotation
+
+ Item {
+ id: inner
+ anchors.fill: parent
+ }
}
states: State {
name: "reparented"
- ParentChange { target: firstChild; parent: otherChild; width: 2*height; rotation: root.myrotation2}
+ ParentChange {
+ target: firstChild
+ parent: otherChild
+ width: 2 *height
+ x: 2 * y
+ rotation: root.myrotation2
+ }
}
Item {
height: parent.height
+ y: parent.y
id: otherChild
}
}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index 176a377ed4..d6814bd057 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -38,6 +38,7 @@
#include <private/qquickitem_p.h>
#include <private/qqmlproperty_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtTest/qsignalspy.h>
class MyAttached : public QObject
{
@@ -199,11 +200,13 @@ private slots:
void revertListMemoryLeak();
void duplicateStateName();
void trivialWhen();
+ void noStateOsciallation();
void parentChangeCorrectReversal();
void revertNullObjectBinding();
void bindableProperties();
void parentChangeInvolvingBindings();
void deferredProperties();
+ void rewindAnchorChange();
};
void tst_qquickstates::initTestCase()
@@ -1715,6 +1718,20 @@ void tst_qquickstates::trivialWhen()
QVERIFY(root);
}
+void tst_qquickstates::noStateOsciallation()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml"));
+ QScopedPointer<QObject> root {component.create()};
+ QVERIFY(root);
+ // set to 1 on initial transition from "" to "n2"
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 1);
+ root->setProperty("number", 1);
+ // setting number to 1 changes directly from "n2" to "n1"
+ // without any intermediate transition to ""
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 2);
+}
+
void tst_qquickstates::parentChangeCorrectReversal()
{
QQmlEngine engine;
@@ -1781,33 +1798,125 @@ void tst_qquickstates::bindableProperties()
}
}
+struct Listener : QQuickItemChangeListener
+{
+ // We want to get notified about all the states.
+ constexpr static const QRectF expectations[] = {
+ QRectF(40, 40, 400, 400),
+ QRectF(40, 0, 400, 400),
+ QRectF(0, 0, 400, 400),
+ QRectF(0, 0, 800, 400),
+ QRectF(0, 0, 800, 200),
+ QRectF(0, 0, 400, 200),
+ QRectF(0, 20, 400, 200),
+ QRectF(40, 20, 400, 200),
+ QRectF(84, 42, 400, 200),
+ QRectF(84, 42, 86, 43),
+ QRectF(40, 40, 86, 43),
+ QRectF(40, 40, 400, 400),
+ QRectF(40, 20, 400, 400),
+ QRectF(40, 20, 400, 200),
+ QRectF(20, 20, 400, 200),
+ QRectF(20, 20, 200, 200),
+ QRectF(20, 20, 200, 300),
+ QRectF(20, 20, 300, 300),
+ QRectF(20, 30, 300, 300),
+ QRectF(30, 30, 300, 300),
+ };
+
+ int position = 0;
+ bool ok = true;
+
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &rect) override
+ {
+ if (rect != expectations[position]) {
+ qDebug() << position << rect;
+ ok = false;
+ }
+ ++position;
+ }
+};
+
void tst_qquickstates::parentChangeInvolvingBindings()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("parentChangeInvolvingBindings.qml"));
QScopedPointer<QQuickItem> root { qobject_cast<QQuickItem *>(c.create()) };
QVERIFY2(root, qPrintable(c.errorString()));
+
+ QObject *child = qmlContext(root.data())->objectForName(QStringLiteral("firstChild"));
+ QVERIFY(child);
+ QQuickItem *childItem = qobject_cast<QQuickItem *>(child);
+ QVERIFY(childItem);
+ Listener listener;
+ QQuickItemPrivate::get(childItem)->addItemChangeListener(&listener, QQuickItemPrivate::Geometry);
+
+
QCOMPARE(root->property("childWidth").toInt(), 400);
+ QCOMPARE(root->property("childX").toInt(), 40);
QCOMPARE(root->property("childRotation").toInt(), 100);
root->setState("reparented");
QCOMPARE(root->property("childWidth").toInt(), 800);
+ QCOMPARE(root->property("childX").toInt(), 0); // x gets zeroed here, from unrelated place.
QCOMPARE(root->property("childRotation").toInt(), 200);
root->setProperty("myrotation2", 300);
root->setHeight(200);
+ root->setY(20);
QCOMPARE(root->property("childRotation").toInt(), 300);
QCOMPARE(root->property("childWidth").toInt(), 400);
+ QCOMPARE(root->property("childX").toInt(), 40);
+
+ QObject *inner = qmlContext(root.data())->objectForName(QStringLiteral("inner"));
+ QVERIFY(inner);
+ QQuickItem *innerItem = qobject_cast<QQuickItem *>(inner);
+ QVERIFY(innerItem);
+
+ QCOMPARE(innerItem->size(), childItem->size());
+
+ // Does not break bindings and does not survive the state change.
+ // However, since the binding between x and y stays intact, we don't know
+ // whether x is set another time from the new y. We pass a pair of numbers that
+ // matches the binding.
+ childItem->setPosition(QPointF(84, 42));
+ QCOMPARE(root->property("childX").toInt(), 84);
+ QVERIFY(listener.ok);
+ childItem->setSize(QSizeF(86, 43));
+ QCOMPARE(root->property("childWidth").toInt(), 86);
+ QVERIFY(listener.ok);
+
+ QCOMPARE(innerItem->size(), childItem->size());
+
+ QSignalSpy xSpy(childItem, SIGNAL(xChanged()));
+ QSignalSpy widthSpy(childItem, SIGNAL(widthChanged()));
root->setState("");
+
+ QVERIFY(listener.ok);
QCOMPARE(root->property("childRotation").toInt(), 100);
- // QCOMPARE(root->property("childWidth").toInt(), 200);
+ // First change to 40 via reverse(), then to 20 via binding.
+ QCOMPARE(xSpy.count(), 2);
+
+ // First change to 400 via reverse(), then to 200 via binding.
+ QCOMPARE(widthSpy.count(), 2);
+
+ QCOMPARE(root->property("childX").toInt(), 20);
+ QCOMPARE(root->property("childWidth").toInt(), 200);
+
+ QCOMPARE(innerItem->size(), childItem->size());
root->setProperty("myrotation", 50);
root->setHeight(300);
+ QVERIFY(listener.ok);
+ root->setY(30);
+ QVERIFY(listener.ok);
QCOMPARE(root->property("childWidth").toInt(), 300);
+ QCOMPARE(root->property("childX").toInt(), 30);
QCOMPARE(root->property("childRotation").toInt(), 50);
+
+ QCOMPARE(innerItem->size(), childItem->size());
}
void tst_qquickstates::deferredProperties()
@@ -1846,6 +1955,29 @@ void tst_qquickstates::deferredProperties()
QCOMPARE(root->height(), 100.0);
}
+void tst_qquickstates::rewindAnchorChange()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("anchorRewind.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> root(c.create());
+ QVERIFY(root);
+
+ QQmlContext *context = qmlContext(root.data());
+ QVERIFY(context);
+
+ QObject *inner = context->objectForName(QStringLiteral("inner"));
+ QVERIFY(inner);
+
+ QQuickItem *innerRect = qobject_cast<QQuickItem *>(inner);
+ QVERIFY(innerRect);
+
+ QTRY_COMPARE(innerRect->x(), 0);
+ QTRY_COMPARE(innerRect->y(), 0);
+ QTRY_COMPARE(innerRect->width(), 200);
+ QTRY_COMPARE(innerRect->height(), 200);
+}
+
QTEST_MAIN(tst_qquickstates)
#include "tst_qquickstates.moc"
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 1ecc3faa3a..cdcd704f98 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -38,6 +38,7 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
+#include <QtQml/qqmlcomponent.h>
#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtQmlModels/private/qqmllistmodel_p.h>
@@ -192,6 +193,10 @@ private slots:
void positionViewAtRow();
void positionViewAtColumn_data();
void positionViewAtColumn();
+ void positionViewAtRowClamped_data();
+ void positionViewAtRowClamped();
+ void positionViewAtColumnClamped_data();
+ void positionViewAtColumnClamped();
void itemAtCell_data();
void itemAtCell();
void leftRightTopBottomProperties_data();
@@ -3065,9 +3070,10 @@ void tst_QQuickTableView::replaceModel()
void tst_QQuickTableView::cellAtPos_data()
{
QTest::addColumn<QPointF>("contentStartPos");
- QTest::addColumn<QPointF>("position");
+ QTest::addColumn<QPointF>("localPos");
QTest::addColumn<bool>("includeSpacing");
QTest::addColumn<QPoint>("expectedCell");
+ QTest::addColumn<QSizeF>("margins");
const int spacing = 10;
const QPointF cellSize(100, 50);
@@ -3080,49 +3086,62 @@ void tst_QQuickTableView::cellAtPos_data()
return QPointF(x, y);
};
- QTest::newRow("1") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0);
- QTest::newRow("2") << QPointF(0, 0) << cellStart(1, 0) << false << QPoint(1, 0);
- QTest::newRow("3") << QPointF(0, 0) << cellStart(0, 1) << false << QPoint(0, 1);
- QTest::newRow("4") << QPointF(0, 0) << cellStart(1, 1) << false << QPoint(1, 1);
+ QTest::newRow("1") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(0, 0);
+ QTest::newRow("2") << QPointF(0, 0) << cellStart(1, 0) << false << QPoint(1, 0) << QSizeF(0, 0);
+ QTest::newRow("3") << QPointF(0, 0) << cellStart(0, 1) << false << QPoint(0, 1) << QSizeF(0, 0);
+ QTest::newRow("4") << QPointF(0, 0) << cellStart(1, 1) << false << QPoint(1, 1) << QSizeF(0, 0);
- QTest::newRow("5") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << false << QPoint(-1, -1);
- QTest::newRow("6") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << false << QPoint(-1, -1);
- QTest::newRow("7") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << false << QPoint(-1, -1);
+ QTest::newRow("5") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0);
+ QTest::newRow("6") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0);
+ QTest::newRow("7") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0);
- QTest::newRow("8") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << true << QPoint(1, 1);
- QTest::newRow("9") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << true << QPoint(0, 0);
- QTest::newRow("10") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << true << QPoint(0, 1);
+ QTest::newRow("8") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << true << QPoint(1, 1) << QSizeF(0, 0);
+ QTest::newRow("9") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << true << QPoint(0, 0) << QSizeF(0, 0);
+ QTest::newRow("10") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << true << QPoint(0, 1) << QSizeF(0, 0);
- QTest::newRow("11") << cellStart(50, 50) << cellStart(0, 0) << false << QPoint(50, 50);
- QTest::newRow("12") << cellStart(50, 50) << cellStart(4, 4) << false << QPoint(54, 54);
- QTest::newRow("13") << cellStart(50, 50) << cellStart(4, 4) - quadSpace << false << QPoint(-1, -1);
- QTest::newRow("14") << cellStart(50, 50) << cellStart(4, 4) + cellSize + quadSpace << false << QPoint(-1, -1);
- QTest::newRow("15") << cellStart(50, 50) << cellStart(4, 4) - quadSpace << true << QPoint(54, 54);
- QTest::newRow("16") << cellStart(50, 50) << cellStart(4, 4) + cellSize + quadSpace << true << QPoint(54, 54);
+ QTest::newRow("11") << cellStart(50, 50) << cellStart(50, 50) << false << QPoint(50, 50) << QSizeF(0, 0);
+ QTest::newRow("12") << cellStart(50, 50) << cellStart(54, 54) << false << QPoint(54, 54) << QSizeF(0, 0);
+ QTest::newRow("13") << cellStart(50, 50) << cellStart(54, 54) - quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0);
+ QTest::newRow("14") << cellStart(50, 50) << cellStart(54, 54) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0);
+ QTest::newRow("15") << cellStart(50, 50) << cellStart(54, 54) - quadSpace << true << QPoint(54, 54) << QSizeF(0, 0);
+ QTest::newRow("16") << cellStart(50, 50) << cellStart(54, 54) + cellSize + quadSpace << true << QPoint(54, 54) << QSizeF(0, 0);
- QTest::newRow("17") << cellStart(50, 50) + halfCell << cellStart(0, 0) << false << QPoint(50, 50);
- QTest::newRow("18") << cellStart(50, 50) + halfCell << cellStart(1, 1) << false << QPoint(51, 51);
- QTest::newRow("19") << cellStart(50, 50) + halfCell << cellStart(4, 4) << false << QPoint(54, 54);
+ QTest::newRow("17") << cellStart(50, 50) + halfCell << cellStart(50, 50) << false << QPoint(50, 50) << QSizeF(0, 0);
+ QTest::newRow("18") << cellStart(50, 50) + halfCell << cellStart(51, 51) << false << QPoint(51, 51) << QSizeF(0, 0);
+ QTest::newRow("19") << cellStart(50, 50) + halfCell << cellStart(54, 54) << false << QPoint(54, 54) << QSizeF(0, 0);
+
+ QTest::newRow("20") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(150, 150);
+ QTest::newRow("20") << QPointF(0, 0) << cellStart(5, 5) << false << QPoint(5, 5) << QSizeF(150, 150);
+
+ QTest::newRow("20") << QPointF(-150, -150) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(150, 150);
+ QTest::newRow("21") << QPointF(-150, -150) << cellStart(4, 0) + halfCell << false << QPoint(4, 0) << QSizeF(150, 150);
+ QTest::newRow("22") << QPointF(-150, -150) << cellStart(0, 4) + halfCell << false << QPoint(0, 4) << QSizeF(150, 150);
+ QTest::newRow("23") << QPointF(-150, -150) << cellStart(4, 4) + halfCell << false << QPoint(4, 4) << QSizeF(150, 150);
}
void tst_QQuickTableView::cellAtPos()
{
QFETCH(QPointF, contentStartPos);
- QFETCH(QPointF, position);
+ QFETCH(QPointF, localPos);
QFETCH(bool, includeSpacing);
QFETCH(QPoint, expectedCell);
+ QFETCH(QSizeF, margins);
LOAD_TABLEVIEW("plaintableview.qml");
auto model = TestModelAsVariant(100, 100);
tableView->setModel(model);
tableView->setRowSpacing(10);
tableView->setColumnSpacing(10);
+ tableView->setLeftMargin(margins.width());
+ tableView->setLeftMargin(margins.height());
+ tableView->setTopMargin(margins.height());
tableView->setContentX(contentStartPos.x());
tableView->setContentY(contentStartPos.y());
WAIT_UNTIL_POLISHED;
- QPoint cell = tableView->cellAtPos(position, includeSpacing);
+ const QPointF posInView = tableView->mapFromItem(tableView->contentItem(), localPos);
+ QPoint cell = tableView->cellAtPos(posInView, includeSpacing);
QCOMPARE(cell, expectedCell);
}
@@ -3138,48 +3157,30 @@ void tst_QQuickTableView::positionViewAtRow_data()
QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << 0. << 50.;
QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << 0. << -1.;
QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << 0. << -1.;
- QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << 0. << -1.;
-
- QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << 0.;
QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 0.;
QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 50.;
QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << -10. << -1.;
- QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << -1.;
- QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << -10. << -1.;
- QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << 0.;
- QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 0.;
- QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 50.;
QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 0. << -1.;
- QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << -1.;
+ QTest::newRow("AlignBottom 98") << 98 << Qt::AlignBottom << 0. << -1.;
QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 0. << -1.;
+ QTest::newRow("AlignBottom 50") << 40 << Qt::AlignBottom << 10. << -1.;
+ QTest::newRow("AlignBottom 40") << 50 << Qt::AlignBottom << -10. << -1.;
+ QTest::newRow("AlignBottom 98") << 98 << Qt::AlignBottom << 10. << -1.;
+ QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << -10. << -1.;
- QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << 0.;
- QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 0.;
- QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 50.;
- QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 10. << -1.;
- QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << -1.;
- QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 10. << -1.;
-
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.;
+ QTest::newRow("AlignCenter 40") << 40 << Qt::AlignCenter << 0. << -1.;
QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.;
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.;
- QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.;
-
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.;
+ QTest::newRow("AlignCenter 40") << 40 << Qt::AlignCenter << 10. << -1.;
QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.;
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.;
- QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.;
}
void tst_QQuickTableView::positionViewAtRow()
{
// Check that positionViewAtRow actually flicks the view
- // to the right position so that the row becomes visible
+ // to the right position so that the row becomes visible.
+ // For this test, we only check cells that can be placed exactly
+ // according to the given alignment.
QFETCH(int, row);
QFETCH(Qt::AlignmentFlag, alignment);
QFETCH(qreal, offset);
@@ -3229,48 +3230,27 @@ void tst_QQuickTableView::positionViewAtColumn_data()
QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << 0. << 50.;
QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << 0. << -1.;
QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << 0. << -1.;
- QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << 0. << -1.;
-
- QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << 0.;
QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 0.;
QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 50.;
QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << -10. << -1.;
- QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << -1.;
- QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << -10. << -1.;
- QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << 0.;
- QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 0.;
- QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 50.;
QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 0. << -1.;
- QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << -1.;
QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 0. << -1.;
-
- QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << 0.;
- QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 0.;
- QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 50.;
QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 10. << -1.;
- QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << -1.;
- QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 10. << -1.;
+ QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << -10. << -1.;
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.;
+ QTest::newRow("AlignCenter 40") << 50 << Qt::AlignCenter << 0. << -1.;
QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.;
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.;
- QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.;
-
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.;
- QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.;
+ QTest::newRow("AlignCenter 40") << 50 << Qt::AlignCenter << 10. << -1.;
QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.;
- QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.;
- QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.;
}
void tst_QQuickTableView::positionViewAtColumn()
{
// Check that positionViewAtColumn actually flicks the view
- // to the right position so that the row becomes visible
+ // to the right position so that the row becomes visible.
+ // For this test, we only check cells that can be placed exactly
+ // according to the given alignment.
QFETCH(int, column);
QFETCH(Qt::AlignmentFlag, alignment);
QFETCH(qreal, offset);
@@ -3308,6 +3288,134 @@ void tst_QQuickTableView::positionViewAtColumn()
}
}
+void tst_QQuickTableView::positionViewAtRowClamped_data()
+{
+ QTest::addColumn<int>("row");
+ QTest::addColumn<Qt::AlignmentFlag>("alignment");
+ QTest::addColumn<qreal>("offset");
+ QTest::addColumn<qreal>("contentYStartPos");
+
+ QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << 0.;
+ QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << -1.;
+ QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << 0. << -1.;
+ QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << -10. << -1.;
+
+ QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << 0.;
+ QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 0.;
+ QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 50.;
+ QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << -1.;
+
+ QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << 0.;
+ QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 0.;
+ QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 50.;
+ QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << -1.;
+ QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 10. << -1.;
+
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.;
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.;
+ QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.;
+
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.;
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.;
+ QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.;
+}
+
+void tst_QQuickTableView::positionViewAtRowClamped()
+{
+ // Check that positionViewAtRow actually flicks the table to the
+ // right position so that the row becomes visible. For this test, we
+ // only test cells that cannot be placed exactly at the given alignment,
+ // because it would cause the table to overshoot. Instead the
+ // table should be flicked to the edge of the viewport, close to the
+ // requested alignment.
+ QFETCH(int, row);
+ QFETCH(Qt::AlignmentFlag, alignment);
+ QFETCH(qreal, offset);
+ QFETCH(qreal, contentYStartPos);
+
+ LOAD_TABLEVIEW("plaintableview.qml");
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ if (contentYStartPos >= 0)
+ tableView->setContentY(contentYStartPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ tableView->positionViewAtRow(row, alignment, offset);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->contentY(), row < 50 ? 0 : tableView->contentHeight() - tableView->height());
+}
+
+void tst_QQuickTableView::positionViewAtColumnClamped_data()
+{
+ QTest::addColumn<int>("column");
+ QTest::addColumn<Qt::AlignmentFlag>("alignment");
+ QTest::addColumn<qreal>("offset");
+ QTest::addColumn<qreal>("contentXStartPos");
+
+ QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << 0.;
+ QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << -1.;
+ QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << 0. << -1.;
+ QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << -10. << -1.;
+
+ QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << 0.;
+ QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 0.;
+ QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 50.;
+ QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << -1.;
+
+ QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << 0.;
+ QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 0.;
+ QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 50.;
+ QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << -1.;
+ QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 10. << -1.;
+
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.;
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.;
+ QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.;
+
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.;
+ QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.;
+ QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.;
+ QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.;
+}
+
+void tst_QQuickTableView::positionViewAtColumnClamped()
+{
+ // Check that positionViewAtColumn actually flicks the table to the
+ // right position so that the column becomes visible. For this test, we
+ // only test cells that cannot be placed exactly at the given alignment,
+ // because it would cause the table to overshoot. Instead the
+ // table should be flicked to the edge of the viewport, close to the
+ // requested alignment.
+ QFETCH(int, column);
+ QFETCH(Qt::AlignmentFlag, alignment);
+ QFETCH(qreal, offset);
+ QFETCH(qreal, contentXStartPos);
+
+ LOAD_TABLEVIEW("plaintableview.qml");
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ if (contentXStartPos >= 0)
+ tableView->setContentX(contentXStartPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ tableView->positionViewAtColumn(column, alignment, offset);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(tableView->contentX(), column < 50 ? 0 : tableView->contentWidth() - tableView->width());
+}
+
void tst_QQuickTableView::itemAtCell_data()
{
QTest::addColumn<QPoint>("cell");
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index bf90a8efd4..c31e698527 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -1028,9 +1028,6 @@ static inline QByteArray msgNotLessThan(int n1, int n2)
void tst_qquicktext::hAlignImplicitWidth()
{
-#ifdef Q_OS_MACOS
- QSKIP("this test currently crashes on MacOS. See QTBUG-68047");
-#endif
QQuickView view(testFileUrl("hAlignImplicitWidth.qml"));
view.setFlags(view.flags() | Qt::WindowStaysOnTopHint); // Prevent being obscured by other windows.
view.show();
@@ -1054,9 +1051,8 @@ void tst_qquicktext::hAlignImplicitWidth()
const int centeredSection3End = centeredSection3 + sectionWidth;
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
// Left Align
QImage image = view.grabWindow();
@@ -4049,7 +4045,11 @@ static qreal expectedBaselineScaled(QQuickText *item)
{
QFont font = item->font();
QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
- do {
+
+ qreal low = 0;
+ qreal high = 10000;
+
+ while (low < high) {
layout.setFont(font);
qreal width = 0;
layout.beginLayout();
@@ -4059,12 +4059,23 @@ static qreal expectedBaselineScaled(QQuickText *item)
}
layout.endLayout();
- if (width < item->width()) {
- QFontMetricsF fm(layout.font());
- return fm.ascent() + item->topPadding();
+ if (width > item->width()) {
+ high = font.pointSizeF();
+ font.setPointSizeF((high + low) / 2);
+ } else {
+ low = font.pointSizeF();
+
+ // When fontSizeMode != FixedSize, the font size will be scaled to a value
+ // The goal is to find a pointSize that uses as much space as possible while
+ // still fitting inside the available space. 0.01 is chosen as the threshold.
+ if ((high - low) < qreal(0.01)) {
+ QFontMetricsF fm(layout.font());
+ return fm.ascent() + item->topPadding();
+ }
+
+ font.setPointSizeF((high + low) / 2);
}
- font.setPointSize(font.pointSize() - 1);
- } while (font.pointSize() > 0);
+ }
return item->topPadding();
}
@@ -4682,9 +4693,8 @@ void tst_qquicktext::verticallyAlignedImageInTable()
void tst_qquicktext::transparentBackground()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QScopedPointer<QQuickView> window(new QQuickView);
window->setSource(testFileUrl("transparentBackground.qml"));
@@ -4703,9 +4713,8 @@ void tst_qquicktext::transparentBackground()
void tst_qquicktext::displaySuperscriptedTag()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QScopedPointer<QQuickView> window(new QQuickView);
window->setSource(testFileUrl("displaySuperscriptedTag.qml"));
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 0fc3db86ae..c15a626d33 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -945,9 +945,8 @@ void tst_qquicktextedit::hAlignVisual()
const int centeredSection3End = centeredSection3 + sectionWidth;
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
// Left Align
QImage image = view.grabWindow();
@@ -2729,7 +2728,8 @@ void tst_qquicktextedit::cursorDelegate()
const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
QTest::qWait(400); //ensure this isn't treated as a double-click
QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, point1);
- QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QMouseEvent mv(QEvent::MouseMove, point2, view.mapToGlobal(point2),
+ Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
QGuiApplication::sendEvent(&view, &mv);
QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, point2);
QTest::qWait(50);
@@ -3766,8 +3766,7 @@ void tst_qquicktextedit::largeTextObservesViewport()
{
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to few fonts installed on offscreen/minimal platforms");
-
+ QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
QFETCH(QString, text);
QFETCH(QQuickTextEdit::TextFormat, textFormat);
QFETCH(bool, parentIsViewport);
@@ -6124,9 +6123,8 @@ void tst_qquicktextedit::keys_shortcutoverride()
void tst_qquicktextedit::transparentSelectionColor()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QQuickView view;
view.setSource(testFileUrl("transparentSelectionColor.qml"));
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index 669f94ed48..42099762f0 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -31,6 +31,7 @@
#include <QtQuickTestUtils/private/testhttpserver_p.h>
#include <private/qinputmethod_p.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlexpression.h>
#include <QFile>
#include <QtQuick/qquickview.h>
@@ -2896,7 +2897,8 @@ void tst_qquicktextinput::cursorDelegate()
const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
QTest::qWait(400); //ensure this isn't treated as a double-click
QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, point1);
- QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
+ QMouseEvent mv(QEvent::MouseMove, point2, textInputObject->mapToGlobal(point2),
+ Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
QGuiApplication::sendEvent(&view, &mv);
QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, point2);
QTest::qWait(50);
diff --git a/tests/auto/quick/qquicktreeview/testmodel.cpp b/tests/auto/quick/qquicktreeview/testmodel.cpp
index a562b3274a..83c7ead4a8 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.cpp
+++ b/tests/auto/quick/qquicktreeview/testmodel.cpp
@@ -61,18 +61,21 @@ TestModel::TestModel(QObject *parent)
m_rootItem.reset(new TreeItem());
for (int col = 0; col < m_columnCount; ++col)
m_rootItem.data()->m_entries << QVariant(QString("0, %1").arg(col));
- createTreeRecursive(m_rootItem.data(), 4, 0, 4);
+ createTreeRecursive(m_rootItem.data(), 4, 1);
}
-void TestModel::createTreeRecursive(TreeItem *item, int childCount, int currentDepth, int maxDepth)
+void TestModel::createTreeRecursive(TreeItem *item, int childCount, int currentDepth)
{
+ if (currentDepth > maxDepth())
+ return;
+
for (int row = 0; row < childCount; ++row) {
auto childItem = new TreeItem(item);
for (int col = 0; col < m_columnCount; ++col)
childItem->m_entries << QVariant(QString("%1, %2").arg(row).arg(col));
item->m_childItems.append(childItem);
- if (currentDepth < maxDepth && row == childCount - 1)
- createTreeRecursive(childItem, childCount, currentDepth + 1, maxDepth);
+ if (row == childCount - 1)
+ createTreeRecursive(childItem, childCount, currentDepth + 1);
}
}
diff --git a/tests/auto/quick/qquicktreeview/testmodel.h b/tests/auto/quick/qquicktreeview/testmodel.h
index 4d9006d112..fc4272af8b 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.h
+++ b/tests/auto/quick/qquicktreeview/testmodel.h
@@ -64,7 +64,7 @@ class TestModel : public QAbstractItemModel
public:
explicit TestModel(QObject *parent = nullptr);
- void createTreeRecursive(TreeItem *item, int childCount, int currentDepth, int maxDepth);
+ void createTreeRecursive(TreeItem *item, int childCount, int currentDepth);
TreeItem *treeItem(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex & = QModelIndex()) const override;
@@ -72,6 +72,7 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
+ int maxDepth() { return 4; }
bool insertRows(int position, int rows, const QModelIndex &parent) override;
diff --git a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
index abba5fced6..712ccdc6cc 100644
--- a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
+++ b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
@@ -90,10 +90,20 @@ private:
private slots:
void initTestCase() override;
void showTreeView();
+ void invalidArguments();
void expandAndCollapseRoot();
void toggleExpanded();
void expandAndCollapseChildren();
void expandChildPendingToBeVisible();
+ void expandRecursivelyRoot_data();
+ void expandRecursivelyRoot();
+ void expandRecursivelyChild_data();
+ void expandRecursivelyChild();
+ void expandRecursivelyWholeTree();
+ void collapseRecursivelyRoot();
+ void collapseRecursivelyChild();
+ void collapseRecursivelyWholeTree();
+ void expandToIndex();
void requiredPropertiesRoot();
void requiredPropertiesChildren();
void emptyModel();
@@ -120,14 +130,47 @@ void tst_qquicktreeview::showTreeView()
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
}
+
+void tst_qquicktreeview::invalidArguments()
+{
+ // Check that we handle gracefully invalid arguments
+ LOAD_TREEVIEW("normaltreeview.qml");
+
+ treeView->expand(-2);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->expandRecursively(200);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->expandRecursively(-2);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->expandRecursively(200);
+ QCOMPARE(treeView->rows(), 1);
+
+ treeView->collapse(-2);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->collapseRecursively(200);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->collapseRecursively(-2);
+ QCOMPARE(treeView->rows(), 1);
+ treeView->collapseRecursively(200);
+ QCOMPARE(treeView->rows(), 1);
+}
+
void tst_qquicktreeview::expandAndCollapseRoot()
{
LOAD_TREEVIEW("normaltreeview.qml");
// Check that the view only has one row loaded so far (the root of the tree)
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
+ QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int)));
+
// Expand the root
treeView->expand(0);
+
+ QCOMPARE(expandedSpy.count(), 1);
+ auto signalArgs = expandedSpy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == 0);
+ QVERIFY(signalArgs.at(1).toInt() == 1);
+
WAIT_UNTIL_POLISHED;
// We now expect 5 rows, the root pluss it's 4 children
QCOMPARE(treeViewPrivate->loadedRows.count(), 5);
@@ -163,14 +206,23 @@ void tst_qquicktreeview::expandAndCollapseChildren()
LOAD_TREEVIEW("normaltreeview.qml");
const int childCount = 4;
+ QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int)));
// Expand the last child of a parent recursively four times
for (int level = 0; level < 4; ++level) {
const int nodeToExpand = level * childCount;
const int firstChildRow = nodeToExpand + 1; // (+ 1 for the root)
const int lastChildRow = firstChildRow + 4;
+
treeView->expand(nodeToExpand);
+
+ QCOMPARE(expandedSpy.count(), 1);
+ auto signalArgs = expandedSpy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == nodeToExpand);
+ QVERIFY(signalArgs.at(1).toInt() == 1);
+
WAIT_UNTIL_POLISHED;
+
QCOMPARE(treeView->rows(), lastChildRow);
auto childItem1 = treeViewPrivate->loadedTableItem(QPoint(0, firstChildRow))->item;
@@ -381,9 +433,305 @@ void tst_qquicktreeview::expandChildPendingToBeVisible()
// Now the view have updated to show
// all the rows that has been expanded.
- QCOMPARE(treeView->rows(), 9);
+ QCOMPARE(treeView->rows(), 9);
+}
+
+void tst_qquicktreeview::expandRecursivelyRoot_data()
+{
+ QTest::addColumn<int>("rowToExpand");
+ QTest::addColumn<int>("depth");
+
+ QTest::newRow("0, -1") << 0 << -1;
+ QTest::newRow("0, 0") << 0 << 0;
+ QTest::newRow("0, 1") << 0 << 1;
+ QTest::newRow("0, 2") << 0 << 2;
+}
+
+void tst_qquicktreeview::expandRecursivelyRoot()
+{
+ // Check that we can expand the root node (row 0), and that
+ // all its children are expanded recursively down to the
+ // given depth.
+ QFETCH(int, rowToExpand);
+ QFETCH(int, depth);
+
+ LOAD_TREEVIEW("normaltreeview.qml");
+ QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+
+ treeView->expandRecursively(rowToExpand, depth);
+
+ if (depth == 0) {
+ QCOMPARE(spy.count(), 0);
+ } else {
+
+ QCOMPARE(spy.count(), 1);
+ const auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == rowToExpand);
+ QVERIFY(signalArgs.at(1).toInt() == depth);
+ }
+
+ WAIT_UNTIL_POLISHED;
+
+ const int rowToExpandDepth = treeView->depth(rowToExpand);
+ const int effectiveMaxDepth = depth != -1 ? rowToExpandDepth + depth : model->maxDepth();
+
+ if (depth > 0 || depth == -1)
+ QVERIFY(treeView->isExpanded(rowToExpand));
+ else
+ QVERIFY(!treeView->isExpanded(rowToExpand));
+
+ // Check that all rows after rowToExpand, that are also
+ // children of that row, is expanded (down to depth)
+ for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) {
+ const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const int currentDepth = treeView->depth(currentRow);
+ const bool isChild = currentDepth > rowToExpandDepth;
+ const bool isExpandable = model->rowCount(modelIndex) > 0;
+ const bool shouldBeExpanded = isChild && isExpandable && currentDepth < effectiveMaxDepth;
+ QCOMPARE(treeView->isExpanded(currentRow), shouldBeExpanded);
+ }
+}
+
+void tst_qquicktreeview::expandRecursivelyChild_data()
+{
+ QTest::addColumn<int>("rowToExpand");
+ QTest::addColumn<int>("depth");
+
+ QTest::newRow("5, -1") << 4 << -1;
+ QTest::newRow("5, 0") << 4 << 0;
+ QTest::newRow("5, 1") << 4 << 1;
+ QTest::newRow("5, 2") << 4 << 2;
+ QTest::newRow("5, 3") << 4 << 3;
+}
+
+void tst_qquicktreeview::expandRecursivelyChild()
+{
+ // Check that we can first expand the root node, and the expand
+ // recursive the first child node with children (row 4), and that all
+ // its children of that node are expanded recursively according to depth.
+ QFETCH(int, rowToExpand);
+ QFETCH(int, depth);
+
+ LOAD_TREEVIEW("normaltreeview.qml");
+ QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+
+ treeView->expand(0);
+
+ QCOMPARE(spy.count(), 1);
+ auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == 0);
+ QVERIFY(signalArgs.at(1).toInt() == 1);
+
+ treeView->expandRecursively(rowToExpand, depth);
+
+ if (depth == 0) {
+ QCOMPARE(spy.count(), 0);
+ } else {
+ QCOMPARE(spy.count(), 1);
+ signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == rowToExpand);
+ QVERIFY(signalArgs.at(1).toInt() == depth);
+ }
+
+ WAIT_UNTIL_POLISHED;
+
+ const bool rowToExpandDepth = treeView->depth(rowToExpand);
+ const int effectiveMaxDepth = depth != -1 ? rowToExpandDepth + depth : model->maxDepth();
+
+ // Check that all rows before rowToExpand is not expanded
+ // (except the root node)
+ for (int currentRow = 1; currentRow < rowToExpand; ++currentRow)
+ QVERIFY(!treeView->isExpanded(currentRow));
+
+ // Check if rowToExpand is expanded
+ if (depth > 0 || depth == -1)
+ QVERIFY(treeView->isExpanded(rowToExpand));
+ else
+ QVERIFY(!treeView->isExpanded(rowToExpand));
+
+ // Check that all rows after rowToExpand that is also
+ // children of that row is expanded (down to depth)
+ for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) {
+ const int currentDepth = treeView->depth(currentRow);
+ const bool isChild = currentDepth > rowToExpandDepth;
+ const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const bool isExpandable = model->rowCount(modelIndex) > 0;
+ const bool shouldBeExpanded = isChild && isExpandable && currentDepth < effectiveMaxDepth;
+ QCOMPARE(treeView->isExpanded(currentRow), shouldBeExpanded);
+ }
}
+void tst_qquicktreeview::expandRecursivelyWholeTree()
+{
+ // Check that we expand the whole tree recursively by passing -1, -1
+ LOAD_TREEVIEW("normaltreeview.qml");
+ QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+ treeView->expandRecursively(-1, -1);
+
+ QCOMPARE(spy.count(), 1);
+ auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == -1);
+ QVERIFY(signalArgs.at(1).toInt() == -1);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that all rows that have children are expanded
+ for (int currentRow = 0; currentRow < treeView->rows(); ++currentRow) {
+ const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const bool isExpandable = model->rowCount(modelIndex) > 0;
+ QCOMPARE(treeView->isExpanded(currentRow), isExpandable);
+ }
+}
+
+void tst_qquicktreeview::collapseRecursivelyRoot()
+{
+ // Check that we can collapse the root node (row 0), and that
+ // all its children are collapsed recursively down to the leaves.
+ LOAD_TREEVIEW("normaltreeview.qml");
+ treeView->expandRecursively();
+ WAIT_UNTIL_POLISHED;
+
+ // Verify that the tree is now fully expanded
+ const int expectedRowCount = 1 + (model->maxDepth() * 4); // root + 4 children per level
+ QCOMPARE(treeView->rows(), expectedRowCount);
+
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+
+ // Collapse the whole tree again. This time, only the root should end up visible
+ treeView->collapseRecursively();
+
+ QCOMPARE(spy.count(), 1);
+ const auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == -1);
+ QVERIFY(signalArgs.at(1).toBool() == true);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->rows(), 1);
+
+ // We need to check that all descendants are collapsed as well. But since they're
+ // now no longer visible in the view, we need to expand each parent one by one again to make
+ // them visible, and check that the child inside that has children is still collapsed.
+ // We can do that by simply iterate over the rows in the view as we expand.
+ int currentRow = 0;
+ while (currentRow < treeView->rows()) {
+ const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0);
+ if (model->hasChildren(currentIndex)) {
+ QVERIFY(!treeView->isExpanded(currentRow));
+ treeView->expand(currentRow);
+ WAIT_UNTIL_POLISHED;
+ }
+ currentRow++;
+ }
+
+ // Sanity check that we ended up with all rows expanded again
+ QCOMPARE(currentRow, expectedRowCount);
+}
+
+void tst_qquicktreeview::collapseRecursivelyChild()
+{
+ // Check that we can collapse a child node (row 4), and that all its children
+ // are collapsed recursively down to the leaves (without touching the root).
+ LOAD_TREEVIEW("normaltreeview.qml");
+ treeView->expandRecursively();
+ WAIT_UNTIL_POLISHED;
+
+ // Verify that the tree is now fully expanded
+ const int expectedRowCount = 1 + (model->maxDepth() * 4); // root + 4 children per level
+ QCOMPARE(treeView->rows(), expectedRowCount);
+
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+
+ // Collapse the 4th child recursive
+ const int rowToCollapse = 4;
+ QCOMPARE(model->data(treeView->modelIndex(rowToCollapse, 0), Qt::DisplayRole), QStringLiteral("3, 0"));
+ treeView->collapseRecursively(rowToCollapse);
+
+ QCOMPARE(spy.count(), 1);
+ const auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == rowToCollapse);
+ QVERIFY(signalArgs.at(1).toBool() == true);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->rows(), 5); // root + 4 children
+
+ // We need to check that all descendants are collapsed as well. But since they're
+ // now no longer visible in the view, we need to expand each parent one by one again to make
+ // them visible, and check that the child inside that has children is still collapsed.
+ // We can do that by simply iterate over the rows in the view as we expand.
+ int currentRow = 1; // start at first child
+ while (currentRow < treeView->rows()) {
+ const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0);
+ if (model->hasChildren(currentIndex)) {
+ QVERIFY(!treeView->isExpanded(currentRow));
+ treeView->expand(currentRow);
+ WAIT_UNTIL_POLISHED;
+ }
+ currentRow++;
+ }
+
+ // Sanity check that we ended up with all rows expanded again
+ QCOMPARE(currentRow, expectedRowCount);
+}
+
+void tst_qquicktreeview::collapseRecursivelyWholeTree()
+{
+ // Check that we collapse the whole tree recursively by passing -1
+ LOAD_TREEVIEW("normaltreeview.qml");
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+ treeView->expandRecursively();
+ treeView->collapseRecursively();
+
+ QCOMPARE(spy.count(), 1);
+ auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == -1);
+ QVERIFY(signalArgs.at(1).toBool() == true);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->rows(), 1); // root
+}
+
+void tst_qquicktreeview::expandToIndex()
+{
+ // Check that expandToIndex(index) expands the tree so
+ // that index becomes visible in the view
+ LOAD_TREEVIEW("normaltreeview.qml");
+ QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+
+ const QModelIndex root = model->index(0, 0);
+ const QModelIndex child1 = model->index(3, 0, root);
+ const QModelIndex child2 = model->index(3, 0, child1);
+
+ QVERIFY(model->hasChildren(root));
+ QVERIFY(model->hasChildren(child1));
+ QVERIFY(model->hasChildren(child2));
+
+ QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(root)));
+ QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child1)));
+ QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child2)));
+
+ const QModelIndex childToExpand = model->index(1, 0, child2);
+ treeView->expandToIndex(childToExpand);
+
+ QVERIFY(treeView->isExpanded(treeView->rowAtIndex(root)));
+ QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child1)));
+ QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child2)));
+
+ QCOMPARE(spy.count(), 1);
+ auto signalArgs = spy.takeFirst();
+ QVERIFY(signalArgs.at(0).toInt() == 0);
+ QVERIFY(signalArgs.at(1).toInt() == 3);
+
+ WAIT_UNTIL_POLISHED;
+
+ // The view should now have 13 rows:
+ // root + 3 expanded nodes that each have 4 children
+ QCOMPARE(treeView->rows(), 13);
+}
+
+
QTEST_MAIN(tst_qquicktreeview)
#include "tst_qquicktreeview.moc"
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index aca156f911..ba09401545 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
#include <QDebug>
+#include <QEvent>
#include <QMimeData>
#include <QTouchEvent>
#include <QtQuick/QQuickItem>
@@ -1618,9 +1619,8 @@ public:
void tst_qquickwindow::earlyGrab()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
qmlRegisterType<Grabber>("Test", 1, 0, "Grabber");
QQmlEngine engine;
@@ -1867,24 +1867,24 @@ void tst_qquickwindow::ignoreUnhandledMouseEvents()
item->setParentItem(window->contentItem());
{
- QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton,
- Qt::NoModifier);
+ QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), window->mapToGlobal(QPointF(50, 50)),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
me.setAccepted(true);
QVERIFY(QCoreApplication::sendEvent(window, &me));
QVERIFY(!me.isAccepted());
}
{
- QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
- Qt::NoModifier);
+ QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), window->mapToGlobal(QPointF(51, 51)),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
me.setAccepted(true);
QVERIFY(QCoreApplication::sendEvent(window, &me));
QVERIFY(!me.isAccepted());
}
{
- QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
- Qt::NoModifier);
+ QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), window->mapToGlobal(QPointF(51, 51)),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
me.setAccepted(true);
QVERIFY(QCoreApplication::sendEvent(window, &me));
QVERIFY(!me.isAccepted());
diff --git a/tests/auto/quickcontrols2/CMakeLists.txt b/tests/auto/quickcontrols2/CMakeLists.txt
index 06eecf6714..b3b655f2cd 100644
--- a/tests/auto/quickcontrols2/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/CMakeLists.txt
@@ -1,7 +1,9 @@
# Generated from auto.pro.
add_subdirectory(accessibility)
+if(NOT ANDROID) # temporarily disabled until QTBUG-100991 is fixed
add_subdirectory(controls)
+endif()
add_subdirectory(cursor)
add_subdirectory(customization)
add_subdirectory(designer)
@@ -24,10 +26,14 @@ add_subdirectory(qquickiconlabel)
add_subdirectory(qquickimaginestyle)
add_subdirectory(qquickmaterialstyle)
add_subdirectory(qquickmaterialstyleconf)
+if(NOT ANDROID) # QTBUG-101005
add_subdirectory(qquickmenu)
+endif()
add_subdirectory(qquickmenubar)
add_subdirectory(qquickninepatchimage)
+if(NOT ANDROID) # QTBUG-101005
add_subdirectory(qquickpopup)
+endif()
add_subdirectory(qquickstyle)
add_subdirectory(qquickuniversalstyle)
add_subdirectory(qquickuniversalstyleconf)
diff --git a/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt b/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt
index 844f0eade5..6ae45f1460 100644
--- a/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt
@@ -20,9 +20,21 @@ qt_internal_add_test(tst_basic
PUBLIC_LIBRARIES
Qt::Gui
Qt::QuickControls2
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_basic
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:basic.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols2/controls/basic/dummy.qml b/tests/auto/quickcontrols2/controls/basic/dummy.qml
deleted file mode 100644
index 6b1bdfec0e..0000000000
--- a/tests/auto/quickcontrols2/controls/basic/dummy.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQml
-import QtQuick
-import QtQuick.NativeStyle
-import QtQuick.Layouts
-import Qt.labs.settings
-import Qt.labs.qmlmodels
-
-QtObject {
-}
diff --git a/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml b/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml
new file mode 100644
index 0000000000..580db56d12
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml
@@ -0,0 +1,12 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+import QtQuick
+import QtQuick.NativeStyle
+import QtQuick.Layouts
+import Qt.labs.settings
+import Qt.labs.qmlmodels
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/controls/data/tst_button.qml b/tests/auto/quickcontrols2/controls/data/tst_button.qml
index a6ec4faad9..f26a050dfc 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_button.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_button.qml
@@ -272,7 +272,9 @@ TestCase {
// no change
sequenceSpy.expectedSequence = []
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
diff --git a/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml b/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml
index be68ac0d1f..ff6b7ec8b8 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml
@@ -313,7 +313,9 @@ TestCase {
// no change
sequenceSpy.expectedSequence = []
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
diff --git a/tests/auto/quickcontrols2/controls/data/tst_combobox.qml b/tests/auto/quickcontrols2/controls/data/tst_combobox.qml
index 100ec45a40..e8e24652de 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_combobox.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_combobox.qml
@@ -641,14 +641,15 @@ TestCase {
}
function test_keys_space_enter_escape_data() {
+ // Not testing Key_Enter + Key_Enter and Key_Return + Key_Return because
+ // QGnomeTheme uses Key_Enter and Key_Return for pressing buttons/comboboxes
+ // and the CI uses the QGnomeTheme platform theme.
return [
{ tag: "space-space", key1: Qt.Key_Space, key2: Qt.Key_Space, showPopup: true, showPress: true, hidePopup: true, hidePress: true },
{ tag: "space-enter", key1: Qt.Key_Space, key2: Qt.Key_Enter, showPopup: true, showPress: true, hidePopup: true, hidePress: true },
{ tag: "space-return", key1: Qt.Key_Space, key2: Qt.Key_Return, showPopup: true, showPress: true, hidePopup: true, hidePress: true },
{ tag: "space-escape", key1: Qt.Key_Space, key2: Qt.Key_Escape, showPopup: true, showPress: true, hidePopup: true, hidePress: false },
{ tag: "space-0", key1: Qt.Key_Space, key2: Qt.Key_0, showPopup: true, showPress: true, hidePopup: false, hidePress: false },
- { tag: "enter-enter", key1: Qt.Key_Enter, key2: Qt.Key_Enter, showPopup: false, showPress: false, hidePopup: true, hidePress: false },
- { tag: "return-return", key1: Qt.Key_Return, key2: Qt.Key_Return, showPopup: false, showPress: false, hidePopup: true, hidePress: false },
{ tag: "escape-escape", key1: Qt.Key_Escape, key2: Qt.Key_Escape, showPopup: false, showPress: false, hidePopup: true, hidePress: false }
]
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml b/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml
index 0e8d188dd2..7350b54320 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml
@@ -307,7 +307,9 @@ TestCase {
// no change
sequenceSpy.expectedSequence = []
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
diff --git a/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml b/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml
index 973e56a360..d0ff56207d 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml
@@ -258,7 +258,9 @@ TestCase {
// no change
sequenceSpy.expectedSequence = []
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
diff --git a/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml b/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml
index d49658762c..d453f017e7 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml
@@ -964,4 +964,38 @@ TestCase {
compare(vertical.visualPosition, 0.2)
compare(vertical.contentItem.y, vertical.topPadding + 0.2 * vertical.availableHeight)
}
+
+ function test_setting_invalid_property_values() {
+ var control = createTemporaryObject(scrollBar, testCase, {size: 2.0, minimumSize: -1.0})
+ verify(control)
+
+ // check that the values are within the expected range
+ compare(control.size, 1.0)
+ compare(control.minimumSize, 0)
+
+ control.minimumSize = 2.0
+ compare(control.minimumSize, 1.0)
+
+ // test if setting NaN is prevented
+ control.size = NaN
+ control.minimumSize = NaN
+ compare(control.size, 1.0)
+ compare(control.minimumSize, 1.0)
+
+
+ // test if setting float infinity is prevented
+ control.size = Number.POSITIVE_INFINITY
+ control.minimumSize = Number.POSITIVE_INFINITY
+ compare(control.size, 1.0)
+ compare(control.minimumSize, 1.0)
+
+ let oldPosition = control.position;
+ let oldStepSize = control.stepSize;
+
+ control.position = NaN;
+ control.stepSize = NaN;
+
+ compare(oldPosition, control.position)
+ compare(oldStepSize, control.stepSize)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_switch.qml b/tests/auto/quickcontrols2/controls/data/tst_switch.qml
index bf25fcacff..a4e1f813a0 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_switch.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_switch.qml
@@ -570,7 +570,9 @@ TestCase {
// no change
spy.expectedSequence = []
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
keyClick(keys[i])
compare(control.checked, false)
diff --git a/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml b/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml
index a7acd05ae4..f85a9193e8 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml
@@ -169,7 +169,9 @@ TestCase {
compare(clickedSpy.count, 2)
// no change
- var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab]
+ // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
+ // pressing buttons and the CI uses the QGnomeTheme platform theme.
+ var keys = [Qt.Key_Escape, Qt.Key_Tab]
for (var i = 0; i < keys.length; ++i) {
keyClick(keys[i])
compare(clickedSpy.count, 2)
diff --git a/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt b/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt
index 633e0442de..6381718103 100644
--- a/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt
@@ -20,9 +20,21 @@ qt_internal_add_test(tst_fusion
PUBLIC_LIBRARIES
Qt::Gui
Qt::QuickControls2
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_fusion
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:fusion.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols2/controls/fusion/dummy.qml b/tests/auto/quickcontrols2/controls/fusion/dummy.qml
deleted file mode 100644
index 6b1bdfec0e..0000000000
--- a/tests/auto/quickcontrols2/controls/fusion/dummy.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQml
-import QtQuick
-import QtQuick.NativeStyle
-import QtQuick.Layouts
-import Qt.labs.settings
-import Qt.labs.qmlmodels
-
-QtObject {
-}
diff --git a/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml b/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml
new file mode 100644
index 0000000000..580db56d12
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml
@@ -0,0 +1,12 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+import QtQuick
+import QtQuick.NativeStyle
+import QtQuick.Layouts
+import Qt.labs.settings
+import Qt.labs.qmlmodels
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt b/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt
index 9638eb8250..962628e1b6 100644
--- a/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt
@@ -20,9 +20,21 @@ qt_internal_add_test(tst_imagine
PUBLIC_LIBRARIES
Qt::Gui
Qt::QuickControls2
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_imagine
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:imagine.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols2/controls/imagine/dummy.qml b/tests/auto/quickcontrols2/controls/imagine/dummy.qml
deleted file mode 100644
index 6b1bdfec0e..0000000000
--- a/tests/auto/quickcontrols2/controls/imagine/dummy.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQml
-import QtQuick
-import QtQuick.NativeStyle
-import QtQuick.Layouts
-import Qt.labs.settings
-import Qt.labs.qmlmodels
-
-QtObject {
-}
diff --git a/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml b/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml
new file mode 100644
index 0000000000..580db56d12
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml
@@ -0,0 +1,12 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+import QtQuick
+import QtQuick.NativeStyle
+import QtQuick.Layouts
+import Qt.labs.settings
+import Qt.labs.qmlmodels
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt b/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt
index da8c14230e..555a11b4ea 100644
--- a/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt
@@ -17,3 +17,13 @@ qt_internal_add_test(tst_macos
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_macos
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
diff --git a/tests/auto/quickcontrols2/controls/material/CMakeLists.txt b/tests/auto/quickcontrols2/controls/material/CMakeLists.txt
index 8cc0cd3fab..b07a63e32d 100644
--- a/tests/auto/quickcontrols2/controls/material/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/material/CMakeLists.txt
@@ -20,9 +20,21 @@ qt_internal_add_test(tst_material
PUBLIC_LIBRARIES
Qt::Gui
Qt::QuickControls2
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_material
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:material.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols2/controls/material/dummy.qml b/tests/auto/quickcontrols2/controls/material/dummy.qml
deleted file mode 100644
index 6b1bdfec0e..0000000000
--- a/tests/auto/quickcontrols2/controls/material/dummy.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQml
-import QtQuick
-import QtQuick.NativeStyle
-import QtQuick.Layouts
-import Qt.labs.settings
-import Qt.labs.qmlmodels
-
-QtObject {
-}
diff --git a/tests/auto/quickcontrols2/controls/material/dummy_imports.qml b/tests/auto/quickcontrols2/controls/material/dummy_imports.qml
new file mode 100644
index 0000000000..580db56d12
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/material/dummy_imports.qml
@@ -0,0 +1,12 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+import QtQuick
+import QtQuick.NativeStyle
+import QtQuick.Layouts
+import Qt.labs.settings
+import Qt.labs.qmlmodels
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt b/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt
index 2faa30cc37..0b3478a227 100644
--- a/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt
@@ -20,9 +20,21 @@ qt_internal_add_test(tst_universal
PUBLIC_LIBRARIES
Qt::Gui
Qt::QuickControls2
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_universal
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:universal.pro:<TRUE>:
# OTHER_FILES = "$$PWD/../data/*.qml"
# TEMPLATE = "app"
diff --git a/tests/auto/quickcontrols2/controls/universal/dummy.qml b/tests/auto/quickcontrols2/controls/universal/dummy.qml
deleted file mode 100644
index 6b1bdfec0e..0000000000
--- a/tests/auto/quickcontrols2/controls/universal/dummy.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import QtQml
-import QtQuick
-import QtQuick.NativeStyle
-import QtQuick.Layouts
-import Qt.labs.settings
-import Qt.labs.qmlmodels
-
-QtObject {
-}
diff --git a/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml b/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml
new file mode 100644
index 0000000000..580db56d12
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml
@@ -0,0 +1,12 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+import QtQuick
+import QtQuick.NativeStyle
+import QtQuick.Layouts
+import Qt.labs.settings
+import Qt.labs.qmlmodels
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt b/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt
index 33755c1bbd..c448c55d25 100644
--- a/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt
@@ -16,3 +16,14 @@ qt_internal_add_test(tst_windows
Qt::QuickControls2
TESTDATA ${test_data}
)
+
+# Make the QML files available to Creator's locator.
+target_sources(tst_windows
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
diff --git a/tests/auto/quickcontrols2/customization/dummy_imports.qml b/tests/auto/quickcontrols2/customization/dummy_imports.qml
new file mode 100644
index 0000000000..40d9cea712
--- /dev/null
+++ b/tests/auto/quickcontrols2/customization/dummy_imports.qml
@@ -0,0 +1,9 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/designer/CMakeLists.txt b/tests/auto/quickcontrols2/designer/CMakeLists.txt
index 50331f8fef..d13b014c93 100644
--- a/tests/auto/quickcontrols2/designer/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/designer/CMakeLists.txt
@@ -11,6 +11,7 @@ qt_internal_add_test(tst_designer
Qt::Gui
Qt::Quick
Qt::QuickControls2
+ Qt::QuickTemplates2
Qt::QuickPrivate
)
diff --git a/tests/auto/quickcontrols2/designer/dummy_imports.qml b/tests/auto/quickcontrols2/designer/dummy_imports.qml
new file mode 100644
index 0000000000..401692e570
--- /dev/null
+++ b/tests/auto/quickcontrols2/designer/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Controls
+
+QtObject { }
diff --git a/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt b/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt
index 926d446f82..cf2d92a567 100644
--- a/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt
@@ -11,6 +11,7 @@ qt_internal_add_test(tst_pressandhold
Qt::Gui
Qt::Quick
Qt::QuickControls2
+ Qt::QuickTemplates2
)
#### Keys ignored in scope 1:.:.:pressandhold.pro:<TRUE>:
diff --git a/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp b/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp
index 1bf3c6e343..89038201cf 100644
--- a/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp
+++ b/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp
@@ -93,10 +93,14 @@ void tst_PressAndHold::pressAndHold()
QVERIFY(spy.isValid() && waitSpy.isValid());
int startDragDistance = QGuiApplication::styleHints()->startDragDistance();
- QMouseEvent press(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
- QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), Qt::RightButton, Qt::RightButton, Qt::NoModifier);
- QMouseEvent move(QEvent::MouseMove, QPointF(2 * startDragDistance, 2 * startDragDistance), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
- QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent press(QEvent::MouseButtonPress, QPointF(), QPointF(),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), QPointF(),
+ Qt::RightButton, Qt::RightButton, Qt::NoModifier);
+ QMouseEvent move(QEvent::MouseMove, QPointF(2 * startDragDistance, 2 * startDragDistance), QPointF(),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), QPointF(),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
// pressAndHold() emitted
QGuiApplication::sendEvent(control.data(), &press);
@@ -166,9 +170,12 @@ void tst_PressAndHold::keepSelection()
QSignalSpy waitSpy(waitControl.data(), SIGNAL(pressAndHold(QQuickMouseEvent*)));
QVERIFY(spy.isValid() && waitSpy.isValid());
- QMouseEvent press(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
- QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), Qt::RightButton, Qt::RightButton, Qt::NoModifier);
- QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent press(QEvent::MouseButtonPress, QPointF(), QPointF(),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), QPointF(),
+ Qt::RightButton, Qt::RightButton, Qt::NoModifier);
+ QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), QPointF(),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QVERIFY(!control->property("text").toString().isEmpty());
QVERIFY(QMetaObject::invokeMethod(control.data(), "selectAll"));
diff --git a/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml b/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml
new file mode 100644
index 0000000000..a4574e460e
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml
@@ -0,0 +1,18 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias control: ctrl
+
+ Control {
+ id: ctrl
+ contentItem: Text {
+ font.pointSize: 10.5
+ elide: Text.ElideRight
+ text: "This is some sample text"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp b/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp
index 94dccf6115..3ee9cc2328 100644
--- a/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp
+++ b/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp
@@ -33,6 +33,7 @@
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
+#include <QtQuick/private/qquicktext_p_p.h>
using namespace QQuickVisualTestUtils;
@@ -46,6 +47,7 @@ public:
private slots:
void initTestCase() override;
void flickable();
+ void fractionalFontSize();
private:
QScopedPointer<QPointingDevice> touchDevice;
@@ -93,6 +95,24 @@ void tst_QQuickControl::flickable()
QTRY_COMPARE(buttonClickedSpy.count(), 1);
}
+void tst_QQuickControl::fractionalFontSize()
+{
+ QQuickApplicationHelper helper(this, QStringLiteral("fractionalFontSize.qml"));
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ const QQuickControl *control = window->property("control").value<QQuickControl *>();
+ QVERIFY(control);
+ QQuickText *contentItem = qobject_cast<QQuickText *>(control->contentItem());
+ QVERIFY(contentItem);
+
+ QVERIFY(!contentItem->truncated());
+
+ QVERIFY2(qFuzzyCompare(contentItem->contentWidth(),
+ QQuickTextPrivate::get(contentItem)->layout.boundingRect().width()),
+ "The QQuickText::contentWidth() doesn't match the layout's preferred text width");
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickControl)
#include "tst_qquickcontrol.moc"
diff --git a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
index edcd1e7173..da3522d89d 100644
--- a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
@@ -34,6 +34,7 @@
#include <QtGui/qstylehints.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtQml/QQmlComponent>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickflickable_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
diff --git a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
index 5f8ad65248..2dcb6a4a18 100644
--- a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
+++ b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
@@ -31,6 +31,7 @@
#include <QAbstractItemModelTester>
#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlComponent>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
diff --git a/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST b/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST
new file mode 100644
index 0000000000..70c935375d
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST
@@ -0,0 +1,15 @@
+# QTBUG-101006
+[nameBindingSourceSize]
+android
+[sourceBindingSourceSize]
+android
+[alignment]
+android
+[color]
+android
+[fileSelectors]
+android
+[imageProvider]
+android
+[translucentColors]
+android
diff --git a/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp b/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp
index c35f8420f7..8c5c8780c9 100644
--- a/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp
+++ b/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp
@@ -410,9 +410,6 @@ void tst_qquickiconimage::color()
{
SKIP_IF_DPR_TOO_HIGH();
- if (QGuiApplication::platformName() == QLatin1String("offscreen"))
- QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)");
-
QQuickView view(testFileUrl("color.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
@@ -468,9 +465,6 @@ void tst_qquickiconimage::fileSelectors()
{
SKIP_IF_DPR_TOO_HIGH();
- if (QGuiApplication::platformName() == QLatin1String("offscreen"))
- QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)");
-
QQuickView view;
QScopedPointer<QQmlFileSelector> fileSelector(new QQmlFileSelector(view.engine()));
fileSelector->setExtraSelectors(QStringList() << "testselector");
@@ -513,9 +507,6 @@ public:
// don't crash (QTBUG-63959)
void tst_qquickiconimage::imageProvider()
{
- if (QGuiApplication::platformName() == QLatin1String("offscreen"))
- QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)");
-
QQuickView view;
view.engine()->addImageProvider("provider", new TestImageProvider);
view.setSource(testFileUrl("imageProvider.qml"));
@@ -545,9 +536,6 @@ void tst_qquickiconimage::imageProvider()
*/
void tst_qquickiconimage::translucentColors()
{
- if (QGuiApplication::platformName() == QLatin1String("offscreen"))
- QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)");
-
// Doesn't reproduce with QQuickView.
QQmlApplicationEngine engine;
engine.load(testFileUrl("translucentColors.qml"));
diff --git a/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml b/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml
index 0d562d1500..8cf09666d0 100644
--- a/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml
+++ b/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml
@@ -1,7 +1,18 @@
-import QtQuick.Controls
import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Controls.impl
+import "sub" as Sub
Item {
Image { source: "a.png" }
IconLabel { icon.source: "a.png" }
+ Button {
+ icon.color: "transparent"
+ icon.source: "a.png"
+ }
+ Button {
+ action: actions.action
+ icon.color: "transparent"
+ Sub.Actions { id: actions }
+ }
}
diff --git a/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml b/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml
new file mode 100644
index 0000000000..20bd6a5e6b
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+QtObject {
+ readonly property Action action: Action { icon.source: "../a.png" }
+}
diff --git a/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp b/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp
index 2ee0bf4c9f..21018d742f 100644
--- a/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp
+++ b/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp
@@ -30,6 +30,7 @@
#include <qtest.h>
+#include <QtQml/QQmlComponent>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitemgrabresult.h>
@@ -37,6 +38,7 @@
#include <QtQuick/private/qquickimage_p_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickicon_p.h>
#include <QtQuickControls2Impl/private/qquickiconimage_p.h>
#include <QtQuickControls2Impl/private/qquickiconlabel_p.h>
@@ -303,9 +305,6 @@ void tst_qquickiconlabel::emptyIconSource()
void tst_qquickiconlabel::colorChanges()
{
- if (QGuiApplication::platformName() == QLatin1String("offscreen"))
- QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)");
-
QQuickView view(testFileUrl("colorChanges.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
@@ -345,7 +344,13 @@ void tst_qquickiconlabel::iconSourceContext()
for (QQuickItem *child : root->childItems()) {
QQuickImage *image = qobject_cast<QQuickImage *>(child);
if (!image) {
- if (QQuickIconLabel *label = qobject_cast<QQuickIconLabel *>(child)) {
+ QQuickIconLabel *label = nullptr;
+ if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(child)) {
+ label = qobject_cast<QQuickIconLabel *>(button->contentItem());
+ } else {
+ label = qobject_cast<QQuickIconLabel *>(child);
+ }
+ if (label) {
QQuickIconLabelPrivate *labelPrivate = static_cast<QQuickIconLabelPrivate *>(
QQuickItemPrivate::get(label));
image = labelPrivate->image;
diff --git a/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp b/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
index 021628709c..0478c97431 100644
--- a/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
+++ b/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
@@ -89,6 +89,8 @@ private slots:
void showTreeView();
void expandAndCollapsUsingDoubleClick();
void expandAndCollapseClickOnIndicator();
+ void expandAndCollapsUsingNonSupportedButtonAndModifers_data();
+ void expandAndCollapsUsingNonSupportedButtonAndModifers();
void checkPropertiesRoot();
void checkPropertiesChildren();
};
@@ -163,6 +165,57 @@ void tst_qquicktreeviewdelegate::expandAndCollapseClickOnIndicator()
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
}
+void tst_qquicktreeviewdelegate::expandAndCollapsUsingNonSupportedButtonAndModifers_data()
+{
+ QTest::addColumn<Qt::MouseButton>("button");
+ QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
+
+ QTest::newRow("left + Qt::ControlModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::ControlModifier);
+ QTest::newRow("left + Qt::ShiftModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::ShiftModifier);
+ QTest::newRow("left + Qt::AltModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::AltModifier);
+ QTest::newRow("left + Qt::MetaModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::MetaModifier);
+ QTest::newRow("left + Qt::ControlModifier + Qt::ShiftModifier") << Qt::LeftButton << (Qt::ShiftModifier | Qt::ControlModifier);
+
+ QTest::newRow("right + Qt::NoModifier") << Qt::RightButton << Qt::KeyboardModifiers(Qt::ControlModifier);
+ QTest::newRow("right + Qt::ControlModifier") << Qt::RightButton << Qt::KeyboardModifiers(Qt::ShiftModifier);
+}
+
+void tst_qquicktreeviewdelegate::expandAndCollapsUsingNonSupportedButtonAndModifers()
+{
+ QFETCH(Qt::MouseButton, button);
+ QFETCH(Qt::KeyboardModifiers, modifiers);
+ // Ensure that we don't expand or collapse the tree if the user is using the right mouse
+ // button, or holding down modifier keys. This "space" is reserved for application specific actions.
+ LOAD_TREEVIEW("unmodified.qml");
+
+ QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
+ const auto item = treeView->itemAtCell(0, 0);
+ QVERIFY(item);
+ const QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
+ const QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint();
+ QTest::mouseDClick(item->window(), button, modifiers, pos);
+
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
+
+ // Expand first row, and ensure we don't collapse it again
+ // if doing a double click together with Qt::CTRL.
+ QTest::mouseDClick(item->window(), Qt::LeftButton, Qt::NoModifier, pos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // We now expect 5 rows, the root pluss it's 4 children
+ QCOMPARE(treeViewPrivate->loadedRows.count(), 5);
+
+ QTest::mouseDClick(item->window(), button, modifiers, pos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // We still expect 5 rows, the root pluss it's 4 children
+ QCOMPARE(treeViewPrivate->loadedRows.count(), 5);
+}
+
void tst_qquicktreeviewdelegate::checkPropertiesRoot()
{
LOAD_TREEVIEW("unmodified.qml");
diff --git a/tests/auto/quickcontrols2/revisions/CMakeLists.txt b/tests/auto/quickcontrols2/revisions/CMakeLists.txt
index 79a0f8e310..801dd6d66d 100644
--- a/tests/auto/quickcontrols2/revisions/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/revisions/CMakeLists.txt
@@ -11,6 +11,7 @@ qt_internal_add_test(tst_revisions
Qt::Gui
Qt::Qml
Qt::QuickControls2
+ Qt::QuickTemplates2
)
## Scopes:
diff --git a/tests/auto/quickcontrols2/snippets/CMakeLists.txt b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
index 1d8ac11673..ce76c4fbc5 100644
--- a/tests/auto/quickcontrols2/snippets/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
@@ -20,6 +20,7 @@ qt_internal_add_test(tst_snippets
Qt::Quick
Qt::QuickControls2
Qt::QuickControls2Private
+ Qt::QuickTemplates2
TESTDATA ${test_data}
)
diff --git a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml
new file mode 100644
index 0000000000..aa37fd14b0
--- /dev/null
+++ b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+QtObject { }
diff --git a/tests/auto/quickdialogs/CMakeLists.txt b/tests/auto/quickdialogs/CMakeLists.txt
index a3b5f15cb3..a8541bb3d5 100644
--- a/tests/auto/quickdialogs/CMakeLists.txt
+++ b/tests/auto/quickdialogs/CMakeLists.txt
@@ -1,4 +1,6 @@
+if(NOT ANDROID) # temporarily disabled until QTBUG-100991 is fixed
add_subdirectory(qquickfiledialogimpl)
add_subdirectory(qquickfolderdialogimpl)
add_subdirectory(qquickfontdialogimpl)
+endif()
add_subdirectory(qquickmessagedialogimpl)
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
index 822b92dd19..1145255781 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
@@ -5,3 +5,6 @@
# QTBUG-92585
[fileMode:OpenFiles]
*
+#QTBUG-101488
+[goUp]
+qnx
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
index 64022f6293..096ebaacb1 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
@@ -21,6 +21,7 @@ qt_internal_add_test(tst_qquickfiledialogimpl
Qt::QuickDialogs2QuickImplPrivate
Qt::QuickDialogs2UtilsPrivate
Qt::QuickPrivate
+ Qt::QuickTemplates2
Qt::QuickTemplates2Private
Qt::QuickTest
Qt::QuickTestUtilsPrivate
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml
new file mode 100644
index 0000000000..4ea33de671
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml
@@ -0,0 +1,9 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt.labs.folderlistmodel
+
+QtObject { }
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index e70c7e7171..a9fa0f21cc 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -123,6 +123,7 @@ void tst_QQuickFileDialogImpl::initTestCase()
QQmlDataTest::initTestCase();
qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", "1");
+ qputenv("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE", "1");
QVERIFY(tempDir.isValid());
// QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml
new file mode 100644
index 0000000000..7faa9b98e0
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml
@@ -0,0 +1,10 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt.labs.folderlistmodel
+
+QtObject { }
diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml
new file mode 100644
index 0000000000..5b8af918ea
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Layouts
+
+QtObject { }
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml
new file mode 100644
index 0000000000..5b8af918ea
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQuick
+import QtQuick.Layouts
+
+QtObject { }
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
index c8900b38de..bca7d7fb00 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
+++ b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
@@ -10,6 +10,11 @@ file(GLOB_RECURSE test_data_glob
${CMAKE_CURRENT_SOURCE_DIR}/data/*.qml)
list(APPEND test_data ${test_data_glob})
+file(GLOB_RECURSE test_imports_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/imports/*)
+list(APPEND test_data ${test_imports_glob})
+
qt_internal_add_test(tst_quicktestmainwithsetup
QMLTEST
SOURCES
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index 91628f0e51..386e72d153 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -3,7 +3,6 @@ add_subdirectory(highdpi)
add_subdirectory(listview)
add_subdirectory(mousearea)
add_subdirectory(nodetypes_ng)
-add_subdirectory(pointer)
# add_subdirectory(qmltypememory) # TODO: C++ API changed
# add_subdirectory(quickwidgetviewer) # TODO: C++ API changed
add_subdirectory(scalablepath)
diff --git a/tests/manual/treeview/sidebyside/data/treeview.qml b/tests/manual/treeview/sidebyside/data/treeview.qml
index 6587e11f6e..a6fd99b076 100644
--- a/tests/manual/treeview/sidebyside/data/treeview.qml
+++ b/tests/manual/treeview/sidebyside/data/treeview.qml
@@ -48,6 +48,7 @@ ApplicationWindow {
visible: true
property alias treeView: treeView
+ property var selectedIndex: undefined
UICallback { id: callback }
@@ -92,6 +93,16 @@ ApplicationWindow {
treeView.model.removeRows(index.row, 1, index.parent);
}
}
+ Button {
+ text: "Expand to"
+ enabled: selectedIndex != undefined
+ onClicked: {
+ treeView.expandToIndex(selectedIndex);
+ treeView.forceLayout()
+ let row = treeView.rowAtIndex(selectedIndex)
+ treeView.positionViewAtRow(row, Qt.AlignVCenter)
+ }
+ }
}
TreeView {
@@ -114,15 +125,28 @@ ApplicationWindow {
id: testModel
}
- Rectangle {
- anchors.fill: parent
- color: "white"
- z: -1
- }
-
Component {
id: treeViewDelegate
TreeViewDelegate {
+ TapHandler {
+ acceptedModifiers: Qt.ControlModifier
+ onTapped: {
+ if (treeView.isExpanded(row))
+ treeView.collapseRecursively(row)
+ else
+ treeView.expandRecursively(row)
+ }
+ }
+ TapHandler {
+ acceptedModifiers: Qt.ShiftModifier
+ onTapped: selectedIndex = treeView.modelIndex(row, 0)
+ }
+ Rectangle {
+ anchors.fill: parent
+ border.color: "red"
+ border.width: 1
+ visible: treeView.modelIndex(row, column) === selectedIndex
+ }
}
}
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 1e8ec2d46f..3392714335 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -61,7 +61,7 @@ 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)
+if(QT_FEATURE_private_tests AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT rtems)
add_subdirectory(qmljs)
endif()
if (QT_FEATURE_private_tests AND NOT CMAKE_CROSSCOMPILING)
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 076ca28b22..42a1745fd8 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -61,6 +61,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
@@ -113,23 +114,23 @@ 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(QDir(QStandardPaths::locate(QStandardPaths::AppConfigLocation, override, QStandardPaths::LocateDirectory)), customConfFileName);
if (fi.exists())
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
else
fi.setFile(override);
if (!fi.exists()) {
@@ -137,7 +138,7 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
exit(1);
}
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
}
}
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index d6869ab05b..7a715d5d13 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -270,19 +270,15 @@ int main(int argc, char **argv)
QQmlJSLogger logger;
// Always trigger the qFatal() on "pragma Strict" violations.
- logger.setCategoryError(Log_Compiler, true);
+ logger.setCategoryLevel(Log_Compiler, QtCriticalMsg);
+ logger.setCategoryIgnored(Log_Compiler, false);
// By default, we're completely silent,
// as the lcAotCompiler category default is QtFatalMsg
- if (lcAotCompiler().isDebugEnabled())
- logger.setCategoryLevel(Log_Compiler, QtDebugMsg);
- else if (lcAotCompiler().isInfoEnabled())
- logger.setCategoryLevel(Log_Compiler, QtInfoMsg);
- else if (lcAotCompiler().isWarningEnabled())
- logger.setCategoryLevel(Log_Compiler, QtWarningMsg);
- else if (lcAotCompiler().isCriticalEnabled())
- logger.setCategoryLevel(Log_Compiler, QtCriticalMsg);
- else
+ const bool loggingEnabled = lcAotCompiler().isDebugEnabled()
+ || lcAotCompiler().isInfoEnabled() || lcAotCompiler().isWarningEnabled()
+ || lcAotCompiler().isCriticalEnabled();
+ if (!loggingEnabled)
logger.setSilent(true);
QQmlJSAotCompiler cppCodeGen(
@@ -297,9 +293,9 @@ int main(int argc, char **argv)
QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
if (!warnings.isEmpty()) {
- logger.logWarning(QStringLiteral("Type warnings occurred while compiling file:"),
- Log_Import);
- logger.processMessages(warnings, QtWarningMsg, Log_Import);
+ logger.log(QStringLiteral("Type warnings occurred while compiling file:"),
+ Log_Import, QQmlJS::SourceLocation());
+ logger.processMessages(warnings, Log_Import);
}
}
} else if (inputFile.endsWith(QLatin1String(".js")) || inputFile.endsWith(QLatin1String(".mjs"))) {
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 605ea03400..24490e1198 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -43,6 +43,7 @@
#include <QtCore/QMetaObject>
#include <QtCore/QMetaProperty>
#include <QtCore/QVariant>
+#include <QtCore/QVariantMap>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt
index cf35319a3b..7e7941539e 100644
--- a/tools/qmltc/CMakeLists.txt
+++ b/tools/qmltc/CMakeLists.txt
@@ -8,21 +8,16 @@ qt_internal_add_tool(${target_name}
qmltcoutputprimitives.h
qmltccodewriter.h qmltccodewriter.cpp
qmltcoutputir.h
- qmltctyperesolver.h
+ qmltctyperesolver.h qmltctyperesolver.cpp
qmltcvisitor.h qmltcvisitor.cpp
qmltccompiler.h qmltccompiler.cpp
qmltccompilerpieces.h
qmltcpropertyutils.h
- prototype/generatedcodeprimitives.h
prototype/qml2cppcontext.h
- prototype/visitor.cpp prototype/visitor.h
prototype/qml2cppdefaultpasses.cpp prototype/qml2cppdefaultpasses.h
prototype/codegenerator.cpp prototype/codegenerator.h
prototype/codegeneratorutil.cpp prototype/codegeneratorutil.h
- prototype/codegeneratorwriter.cpp prototype/codegeneratorwriter.h
- prototype/qmlcompiler.h
- prototype/typeresolver.cpp prototype/typeresolver.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index c2002235cf..49e218f4fe 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -28,8 +28,10 @@
#include "qmltccommandlineutils.h"
#include "prototype/codegenerator.h"
-#include "prototype/visitor.h"
-#include "prototype/typeresolver.h"
+#include "qmltcvisitor.h"
+#include "qmltctyperesolver.h"
+
+#include "qmltccompiler.h"
#include <QtQml/private/qqmlirbuilder_p.h>
#include <private/qqmljscompiler_p.h>
@@ -46,8 +48,18 @@
void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
{
- // TODO: support object bindings and change to setCategoryLevel(QtInfoMsg)
- logger.setCategoryError(Log_Compiler, true);
+ const QSet<QQmlJSLoggerCategory> exceptions {
+ Log_ControlsSanity, // this category is just weird
+ Log_UnusedImport, // not critical
+ };
+
+ for (int i = 0; i <= static_cast<int>(QQmlJSLoggerCategory_Last); ++i) {
+ const auto c = static_cast<QQmlJSLoggerCategory>(i);
+ if (exceptions.contains(c))
+ continue;
+ logger.setCategoryLevel(c, QtCriticalMsg);
+ logger.setCategoryIgnored(c, false);
+ }
}
int main(int argc, char **argv)
@@ -175,11 +187,11 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- Options options;
- options.outputCppFile = parser.value(outputCppOption);
- options.outputHFile = parser.value(outputHOption);
- options.resourcePath = paths.first();
- options.outNamespace = parser.value(namespaceOption);
+ QmltcCompilerInfo info;
+ info.outputCppFile = parser.value(outputCppOption);
+ info.outputHFile = parser.value(outputHOption);
+ info.resourcePath = paths.first();
+ info.outputNamespace = parser.value(namespaceOption);
QQmlJSImporter importer { importPaths, &mapper };
QQmlJSLogger logger;
@@ -187,29 +199,27 @@ int main(int argc, char **argv)
logger.setCode(sourceCode);
setupLogger(logger);
- Qmltc::Visitor visitor(&importer, &logger,
- QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);
- Qmltc::TypeResolver typeResolver { &importer };
+ QmltcVisitor visitor(&importer, &logger,
+ QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);
+ QmltcTypeResolver typeResolver { &importer };
typeResolver.init(visitor, document.program);
- if (logger.hasWarnings() || logger.hasErrors())
+ if (logger.hasErrors())
return EXIT_FAILURE;
- CodeGenerator generator(url, &logger, &document, &typeResolver);
- generator.generate(options);
-
-# if 0 // TODO: Currently disabled due to QTBUG-100103, remove this #if guard once the issue has
- // been addressed
QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
-
if (!warnings.isEmpty()) {
- logger.logWarning(QStringLiteral("Type warnings occurred while compiling file:"),
- Log_Import);
- logger.processMessages(warnings, QtWarningMsg, Log_Import);
+ logger.log(QStringLiteral("Type warnings occurred while compiling file:"), Log_Import,
+ QQmlJS::SourceLocation());
+ logger.processMessages(warnings, Log_Import);
+ // Log_Import is critical for the compiler
+ return EXIT_FAILURE;
}
-# endif
- if (logger.hasWarnings() || logger.hasErrors())
+ CodeGenerator generator(url, &logger, &document, &typeResolver, &info);
+ generator.generate();
+
+ if (logger.hasErrors())
return EXIT_FAILURE;
return EXIT_SUCCESS;
diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp
index ff71db989b..cf1bb719ee 100644
--- a/tools/qmltc/prototype/codegenerator.cpp
+++ b/tools/qmltc/prototype/codegenerator.cpp
@@ -30,7 +30,9 @@
#include "prototype/qml2cppdefaultpasses.h"
#include "prototype/qml2cpppropertyutils.h"
#include "prototype/codegeneratorutil.h"
-#include "prototype/codegeneratorwriter.h"
+#include "qmltccodewriter.h"
+
+#include "qmltccompiler.h"
#include <QtCore/qfileinfo.h>
#include <QtCore/qhash.h>
@@ -45,12 +47,7 @@
#include <utility>
#include <numeric>
-static constexpr char newLineLatin1[] =
-#ifdef Q_OS_WIN32
- "\r\n";
-#else
- "\n";
-#endif
+QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcCodeGenerator, "qml.qmltc.compiler", QtWarningMsg);
@@ -91,12 +88,12 @@ static QString figureReturnType(const QQmlJSMetaMethod &m)
return type;
}
-static QList<QQmlJSAotVariable>
+static QList<QmltcVariable>
compileMethodParameters(const QStringList &names,
const QList<QSharedPointer<const QQmlJSScope>> &types,
bool allowUnnamed = false)
{
- QList<QQmlJSAotVariable> paramList;
+ QList<QmltcVariable> paramList;
const auto size = names.size();
paramList.reserve(size);
for (qsizetype i = 0; i < size; ++i) {
@@ -105,8 +102,7 @@ compileMethodParameters(const QStringList &names,
Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified
if (name.isEmpty() && allowUnnamed)
name = u"unnamed_" + QString::number(i);
- paramList.emplaceBack(
- QQmlJSAotVariable { types[i]->augmentedInternalName(), name, QString() });
+ paramList.emplaceBack(QmltcVariable { types[i]->augmentedInternalName(), name, QString() });
}
return paramList;
}
@@ -216,13 +212,12 @@ toOrderedSequence(typename QmlIR::PoolList<QmlIR::Binding>::Iterator first,
Q_LOGGING_CATEGORY(lcCodeGen, "qml.compiler.CodeGenerator", QtWarningMsg);
CodeGenerator::CodeGenerator(const QString &url, QQmlJSLogger *logger, QmlIR::Document *doc,
- const Qmltc::TypeResolver *localResolver)
- : m_url(url),
- m_logger(logger),
- m_doc(doc),
- m_localTypeResolver(localResolver),
- m_qmlSource(doc->code.split(QLatin1String(newLineLatin1)))
+ const QmltcTypeResolver *localResolver, const QmltcCompilerInfo *info)
+ : m_url(url), m_logger(logger), m_doc(doc), m_localTypeResolver(localResolver), m_info(info)
{
+ Q_ASSERT(m_info);
+ Q_ASSERT(!m_info->outputHFile.isEmpty());
+ Q_ASSERT(!m_info->outputCppFile.isEmpty());
}
void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes)
@@ -291,21 +286,18 @@ void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes)
m_ignoredTypes = collectIgnoredTypes(context, objects);
};
executor.addPass(setIgnoredTypes);
+ executor.addPass(&setDeferredBindings);
// run all passes:
executor.run(m_logger);
}
-void CodeGenerator::generate(const Options &options)
+void CodeGenerator::generate()
{
- m_options = options;
- GeneratedCode code;
const QString rootClassName = QFileInfo(m_url).baseName();
Q_ASSERT(!rootClassName.isEmpty());
- Q_ASSERT(!options.outputHFile.isEmpty());
- Q_ASSERT(!options.outputCppFile.isEmpty());
- const QString hPath = options.outputHFile;
- const QString cppPath = options.outputCppFile;
+ const QString hPath = m_info->outputHFile;
+ const QString cppPath = m_info->outputCppFile;
m_isAnonymous = rootClassName.at(0).isLower();
const QString url = QFileInfo(m_url).fileName();
@@ -315,7 +307,7 @@ void CodeGenerator::generate(const Options &options)
QSet<QString> requiredCppIncludes;
constructObjects(requiredCppIncludes); // this populates all the codegen objects
// no point in compiling anything if there are errors
- if (m_logger->hasErrors() || m_logger->hasWarnings())
+ if (m_logger->hasErrors())
return;
if (m_objects.isEmpty()) {
@@ -329,16 +321,16 @@ void CodeGenerator::generate(const Options &options)
};
const auto &root = m_objects.at(0).type;
- QList<QQmlJSAotObject> compiledObjects;
+ QList<QmltcType> compiledObjects;
if (isComponent(root)) {
compiledObjects.reserve(1);
compiledObjects.emplaceBack(); // create new object
- const auto compile = [this](QQmlJSAotObject &current, const CodeGenObject &object) {
+ const auto compile = [this](QmltcType &current, const CodeGenObject &object) {
this->compileQQmlComponentElements(current, object);
};
compileObject(compiledObjects.back(), m_objects.at(0), compile);
} else {
- const auto compile = [this](QQmlJSAotObject &current, const CodeGenObject &object) {
+ const auto compile = [this](QmltcType &current, const CodeGenObject &object) {
this->compileObjectElements(current, object);
};
@@ -362,18 +354,21 @@ void CodeGenerator::generate(const Options &options)
}
}
// no point in generating anything if there are errors
- if (m_logger->hasErrors() || m_logger->hasWarnings())
+ if (m_logger->hasErrors())
return;
- QQmlJSProgram program { compiledObjects, m_urlMethod, url, hPath, cppPath,
- options.outNamespace, requiredCppIncludes };
+ QmltcProgram program {
+ url, cppPath, hPath, m_info->outputNamespace, requiredCppIncludes,
+ m_urlMethod, compiledObjects
+ };
// write everything
- GeneratedCodeUtils codeUtils(code);
- CodeGeneratorWriter::write(codeUtils, program);
+ QmltcOutput code;
+ QmltcOutputWrapper codeUtils(code);
+ QmltcCodeWriter::write(codeUtils, program);
writeToFile(hPath, code.header.toUtf8());
- writeToFile(cppPath, code.implementation.toUtf8());
+ writeToFile(cppPath, code.cpp.toUtf8());
}
QString buildCallSpecialMethodValue(bool documentRoot, const QString &outerFlagName,
@@ -388,9 +383,14 @@ QString buildCallSpecialMethodValue(bool documentRoot, const QString &outerFlagN
}
void CodeGenerator::compileObject(
- QQmlJSAotObject &compiled, const CodeGenObject &object,
- std::function<void(QQmlJSAotObject &, const CodeGenObject &)> compileElements)
+ QmltcType &compiled, const CodeGenObject &object,
+ std::function<void(QmltcType &, const CodeGenObject &)> compileElements)
{
+ if (object.type->isSingleton()) {
+ recordError(object.type->sourceLocation(), u"Singleton types are not supported"_qs);
+ return;
+ }
+
compiled.cppType = object.type->internalName();
const QString baseClass = object.type->baseType()->internalName();
@@ -428,14 +428,14 @@ void CodeGenerator::compileObject(
compiled.handleOnCompleted.name = u"QML_handleOnCompleted"_qs;
compiled.handleOnCompleted.returnType = u"void"_qs;
- QQmlJSAotVariable engine(u"QQmlEngine *"_qs, u"engine"_qs, QString());
- QQmlJSAotVariable parent(u"QObject *"_qs, u"parent"_qs, u"nullptr"_qs);
+ QmltcVariable engine(u"QQmlEngine *"_qs, u"engine"_qs, QString());
+ QmltcVariable parent(u"QObject *"_qs, u"parent"_qs, u"nullptr"_qs);
compiled.baselineCtor.parameterList = { parent };
compiled.externalCtor.parameterList = { engine, parent };
- QQmlJSAotVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData> &"_qs, u"parentContext"_qs,
- QString());
- QQmlJSAotVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs, QString());
- QQmlJSAotVariable callSpecialMethodFlag(u"bool"_qs, u"callSpecialMethodNow"_qs, QString());
+ QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData> &"_qs, u"parentContext"_qs,
+ QString());
+ QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs, QString());
+ QmltcVariable callSpecialMethodFlag(u"bool"_qs, u"callSpecialMethodNow"_qs, QString());
if (documentRoot) {
compiled.init.parameterList = { engine, ctxtdata, finalizeFlag, callSpecialMethodFlag };
compiled.endInit.parameterList = { engine, finalizeFlag };
@@ -645,6 +645,17 @@ void CodeGenerator::compileObject(
+ u"(engine, /* finalize */ false);";
compiled.endInit.body << u"}"_qs;
}
+
+ if (object.irObject->flags & QV4::CompiledData::Object::HasDeferredBindings) {
+ compiled.endInit.body << u"{ // defer bindings"_qs;
+ compiled.endInit.body << u"auto ddata = QQmlData::get(this);"_qs;
+ compiled.endInit.body << u"auto thisContext = ddata->outerContext;"_qs;
+ compiled.endInit.body << u"Q_ASSERT(thisContext);"_qs;
+ compiled.endInit.body << u"ddata->deferData(" + QString::number(objectIndex) + u", "
+ + CodeGeneratorUtility::compilationUnitVariable.name + u", thisContext);";
+ compiled.endInit.body << u"}"_qs;
+ }
+
// TODO: decide whether begin/end property update group is needed
// compiled.endInit.body << u"Qt::beginPropertyUpdateGroup(); // defer binding evaluation"_qs;
@@ -682,20 +693,8 @@ void CodeGenerator::compileObject(
// compiled.endInit.body << u"Qt::endPropertyUpdateGroup();"_qs;
}
-void CodeGenerator::compileObjectElements(QQmlJSAotObject &compiled, const CodeGenObject &object)
+void CodeGenerator::compileObjectElements(QmltcType &compiled, const CodeGenObject &object)
{
- if (object.type->isSingleton()) {
- if (m_isAnonymous) {
- recordError(object.type->sourceLocation(),
- QStringLiteral(u"This singleton type won't be accessible from the outside. "
- "Consider changing the file name so that it starts with a "
- "capital letter."));
- return;
- }
- compiled.mocCode << u"QML_SINGLETON"_qs;
- compiled.externalCtor.access = QQmlJSMetaMethod::Private;
- }
-
// compile enums
const auto enums = object.type->ownEnumerations();
compiled.enums.reserve(enums.size());
@@ -787,15 +786,9 @@ void CodeGenerator::compileObjectElements(QQmlJSAotObject &compiled, const CodeG
compileBinding(compiled, **it, object, { object.type, u"this"_qs, u""_qs, false });
}
-void CodeGenerator::compileQQmlComponentElements(QQmlJSAotObject &compiled,
- const CodeGenObject &object)
+void CodeGenerator::compileQQmlComponentElements(QmltcType &compiled, const CodeGenObject &object)
{
- if (object.type->isSingleton()) {
- // it is unclear what to do with singletons in general, so just reject
- recordError(object.type->sourceLocation(),
- QStringLiteral(u"Singleton Component-based types are not supported"));
- return;
- }
+ Q_UNUSED(object);
// since we create a document root as QQmlComponent, we only need to fake
// QQmlComponent construction in init:
@@ -827,7 +820,7 @@ void CodeGenerator::compileQQmlComponentElements(QQmlJSAotObject &compiled,
compiled.init.body << u"}"_qs;
}
-void CodeGenerator::compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &e)
+void CodeGenerator::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
{
const auto intValues = e.values();
QStringList values;
@@ -840,7 +833,7 @@ void CodeGenerator::compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &
u"Q_ENUM(%1)"_qs.arg(e.name()));
}
-void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaProperty &p,
+void CodeGenerator::compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
const QQmlJSScope::ConstPtr &owner)
{
Q_ASSERT(!p.isAlias()); // will be handled separately
@@ -872,10 +865,10 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
// 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() && !p.isList()) {
- QQmlJSAotMethod setter {};
+ QmltcMethod setter {};
setter.returnType = u"void"_qs;
setter.name = compilationData.write;
- // QQmlJSAotVariable
+ // QmltcVariable
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
u""_qs);
setter.body << variableName + u".setValue(" + name + u"_);";
@@ -885,7 +878,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
mocPieces << u"WRITE"_qs << setter.name;
}
- QQmlJSAotMethod getter {};
+ QmltcMethod getter {};
getter.returnType = underlyingType;
getter.name = compilationData.read;
getter.body << u"return " + variableName + u".value();";
@@ -895,7 +888,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
// 2. add bindable
if (!p.isList()) {
- QQmlJSAotMethod bindable {};
+ QmltcMethod bindable {};
bindable.returnType = u"QBindable<" + underlyingType + u">";
bindable.name = compilationData.bindable;
bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
@@ -923,7 +916,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
compilationData.notify);
}
-void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaProperty &alias,
+void CodeGenerator::compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner)
{
const QString aliasName = alias.propertyName();
@@ -1047,7 +1040,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
Qml2CppPropertyData compilationData(aliasName);
// 1. add setter and getter
if (!info.readLine.isEmpty()) {
- QQmlJSAotMethod getter {};
+ QmltcMethod getter {};
getter.returnType = info.underlyingType;
getter.name = compilationData.read;
getter.body += prologue;
@@ -1059,13 +1052,13 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
} // else always an error?
if (!info.writeLine.isEmpty()) {
- QQmlJSAotMethod setter {};
+ QmltcMethod setter {};
setter.returnType = u"void"_qs;
setter.name = compilationData.write;
QList<QQmlJSMetaMethod> methods = type->methods(resultingProperty.write());
if (methods.isEmpty()) {
- // QQmlJSAotVariable
+ // QmltcVariable
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(info.underlyingType),
aliasName + u"_", u""_qs);
} else {
@@ -1079,7 +1072,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
parameterNames.reserve(setter.parameterList.size());
std::transform(setter.parameterList.cbegin(), setter.parameterList.cend(),
std::back_inserter(parameterNames),
- [](const QQmlJSAotVariable &x) { return x.name; });
+ [](const QmltcVariable &x) { return x.name; });
QString commaSeparatedParameterNames = parameterNames.join(u", "_qs);
setter.body << info.writeLine.arg(commaSeparatedParameterNames) + u";";
} else {
@@ -1093,7 +1086,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
// 2. add bindable
if (!info.bindableLine.isEmpty()) {
- QQmlJSAotMethod bindable {};
+ QmltcMethod bindable {};
bindable.returnType = u"QBindable<" + info.underlyingType + u">";
bindable.name = compilationData.bindable;
bindable.body += prologue;
@@ -1129,7 +1122,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
}
}
-void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMethod &m,
+void CodeGenerator::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
const QmlIR::Function *f, const CodeGenObject &object)
{
Q_UNUSED(object);
@@ -1138,7 +1131,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
const auto paramNames = m.parameterNames();
const auto paramTypes = m.parameterTypes();
Q_ASSERT(paramNames.size() == paramTypes.size());
- const QList<QQmlJSAotVariable> paramList = compileMethodParameters(paramNames, paramTypes);
+ const QList<QmltcVariable> paramList = compileMethodParameters(paramNames, paramTypes);
const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
@@ -1153,7 +1146,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
returnType, paramList);
}
- QQmlJSAotMethod compiled {};
+ QmltcMethod compiled {};
compiled.returnType = returnType;
compiled.name = m.methodName();
compiled.parameterList = std::move(paramList);
@@ -1161,7 +1154,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
compiled.type = methodType;
compiled.access = m.access();
if (methodType != QQmlJSMetaMethod::Signal) {
- compiled.declPreambles << u"Q_INVOKABLE"_qs; // TODO: do we need this for signals as well?
+ compiled.declarationPrefixes << u"Q_INVOKABLE"_qs;
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
} else {
compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal();
@@ -1176,10 +1169,33 @@ static QString getPropertyOrAliasNameFromIr(const QmlIR::Document *doc, Iterator
return doc->stringAt(first->nameIndex);
}
-void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
+void CodeGenerator::compileBinding(QmltcType &current, const QmlIR::Binding &binding,
const CodeGenObject &object,
const CodeGenerator::AccessorData &accessor)
{
+ // 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 (binding.flags & QV4::CompiledData::Binding::IsDeferredBinding) {
+ if (binding.type == QmlIR::Binding::Type_GroupProperty) {
+ // TODO: we should warn about this in QmlCompiler library
+ qCWarning(lcCodeGenerator)
+ << QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "
+ "binding on a group property.")
+ .arg(QString::number(binding.location.line),
+ QString::number(binding.location.column));
+ // we do not support PropertyChanges and other types with similar
+ // behavior yet, so this binding is compiled
+ } else {
+ qCDebug(lcCodeGenerator)
+ << QStringLiteral(
+ "Binding at line %1 column %2 is deferred and thus not compiled")
+ .arg(QString::number(binding.location.line),
+ QString::number(binding.location.column));
+ return;
+ }
+ }
+
// TODO: cache property name somehow, so we don't need to look it up again
QString propertyName = m_doc->stringAt(binding.propertyNameIndex);
if (propertyName.isEmpty()) {
@@ -1401,6 +1417,9 @@ void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Bindin
std::for_each(irObject->bindingsBegin(), irObject->bindingsEnd(), compileComponent);
} else {
const QString attachingTypeName = propertyName; // acts as an identifier
+ auto attachingType = m_localTypeResolver->typeForName(attachingTypeName);
+ Q_ASSERT(attachingType); // an error somewhere else
+
QString attachedTypeName = type->attachedTypeName(); // TODO: check if == internalName?
if (attachedTypeName.isEmpty()) // TODO: shouldn't happen ideally
attachedTypeName = type->baseTypeName();
@@ -1413,7 +1432,7 @@ void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Bindin
u"nullptr"_qs);
// Note: getting attached property is fairly expensive
const QString getAttachedPropertyLine = u"qobject_cast<" + attachedTypeName
- + u" *>(qmlAttachedPropertiesObject<" + attachedTypeName
+ + u" *>(qmlAttachedPropertiesObject<" + attachingType->internalName()
+ u">(this, /* create = */ true))";
current.endInit.body
<< attachedMemberName + u" = " + getAttachedPropertyLine + u";";
@@ -1422,9 +1441,10 @@ void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Bindin
// compile bindings of the attached property
auto sortedBindings = toOrderedSequence(
irObject->bindingsBegin(), irObject->bindingsEnd(), irObject->bindingCount());
- for (auto it : qAsConst(sortedBindings))
+ for (auto it : qAsConst(sortedBindings)) {
compileBinding(current, *it, attachedObject,
{ object.type, attachedMemberName, propertyName, false });
+ }
}
break;
}
@@ -1559,26 +1579,26 @@ QString CodeGenerator::makeGensym(const QString &base)
}
// returns compiled script binding for "property changed" handler in a form of object type
-static QQmlJSAotObject compileScriptBindingPropertyChangeHandler(
+static QmltcType compileScriptBindingPropertyChangeHandler(
const QmlIR::Document *doc, const QmlIR::Binding &binding, const QmlIR::Object *irObject,
- const QQmlJSAotMethod &urlMethod, const QString &functorCppType,
- const QString &objectCppType, const QList<QQmlJSAotVariable> &slotParameters)
+ const QmltcMethod &urlMethod, const QString &functorCppType, const QString &objectCppType,
+ const QList<QmltcVariable> &slotParameters)
{
- QQmlJSAotObject bindingFunctor {};
+ QmltcType bindingFunctor {};
bindingFunctor.cppType = functorCppType;
bindingFunctor.ignoreInit = true;
// default member variable and ctor:
const QString pointerToObject = objectCppType + u" *";
bindingFunctor.variables.emplaceBack(
- QQmlJSAotVariable { pointerToObject, u"m_self"_qs, u"nullptr"_qs });
+ QmltcVariable { pointerToObject, u"m_self"_qs, u"nullptr"_qs });
bindingFunctor.baselineCtor.name = functorCppType;
bindingFunctor.baselineCtor.parameterList.emplaceBack(
- QQmlJSAotVariable { pointerToObject, u"self"_qs, QString() });
+ QmltcVariable { pointerToObject, u"self"_qs, QString() });
bindingFunctor.baselineCtor.initializerList.emplaceBack(u"m_self(self)"_qs);
// call operator:
- QQmlJSAotMethod callOperator {};
+ QmltcMethod callOperator {};
callOperator.returnType = u"void"_qs;
callOperator.name = u"operator()"_qs;
callOperator.parameterList = slotParameters;
@@ -1608,7 +1628,7 @@ propertyForChangeHandler(const QQmlJSScope::ConstPtr &scope, QString name)
return {};
}
-void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
+void CodeGenerator::compileScriptBinding(QmltcType &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName,
const CodeGenObject &object, const QString &propertyName,
const QQmlJSScope::ConstPtr &propertyType,
@@ -1647,7 +1667,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
};
// these only make sense when binding is on signal handler
- QList<QQmlJSAotVariable> slotParameters;
+ QList<QmltcVariable> slotParameters;
QString signalName;
QString signalReturnType;
@@ -1734,15 +1754,8 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
Q_ASSERT(!objectClassName_slot.isEmpty());
const QString slotName = makeGensym(signalName + u"_slot");
- if (objectType->isSingleton()) { // TODO: support
- recordError(binding.location,
- u"Binding on singleton type '" + objectClassName_signal
- + u"' is not supported");
- return;
- }
-
// SignalHander specific:
- QQmlJSAotMethod slotMethod {};
+ QmltcMethod slotMethod {};
slotMethod.returnType = signalReturnType;
slotMethod.name = slotName;
slotMethod.parameterList = slotParameters;
@@ -1819,7 +1832,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
+ accessor.name + u"))));";
current.variables.emplaceBack(
- QQmlJSAotVariable { typeOfQmlBinding, bindingSymbolName, QString() });
+ QmltcVariable { typeOfQmlBinding, bindingSymbolName, QString() });
// current.ctor.initializerList << bindingSymbolName + u"()";
break;
}
@@ -1827,7 +1840,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
}
// TODO: should use "compileScriptBinding" instead of custom code
-void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
+void CodeGenerator::compileScriptBindingOfComponent(QmltcType &current,
const QmlIR::Object *irObject,
const QQmlJSScope::ConstPtr objectType,
const QmlIR::Binding &binding,
@@ -1867,7 +1880,7 @@ void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
const QString slotName = makeGensym(signalName + u"_slot");
// SignalHander specific:
- QQmlJSAotMethod slotMethod {};
+ QmltcMethod slotMethod {};
slotMethod.returnType = signalReturnType;
slotMethod.name = slotName;
@@ -1885,7 +1898,8 @@ void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
current.handleOnCompleted.body << slotName + u"();";
} else if (signalName == u"destruction"_qs) {
if (!current.dtor) {
- current.dtor = QQmlJSAotSpecialMethod {};
+ // TODO: double-check that this stuff is actually correct now:
+ current.dtor = QmltcDtor {};
current.dtor->name = u"~" + current.cppType;
}
current.dtor->firstLines << slotName + u"();";
@@ -1898,18 +1912,20 @@ void CodeGenerator::compileUrlMethod()
m_urlMethod.returnType = u"const QUrl &"_qs;
m_urlMethod.name = u"q_qmltc_docUrl"_qs;
m_urlMethod.body << u"static QUrl docUrl = %1;"_qs.arg(
- CodeGeneratorUtility::toResourcePath(m_options.resourcePath));
+ CodeGeneratorUtility::toResourcePath(m_info->resourcePath));
m_urlMethod.body << u"return docUrl;"_qs;
- m_urlMethod.declPreambles << u"static"_qs;
+ m_urlMethod.declarationPrefixes << u"static"_qs;
m_urlMethod.modifiers << u"noexcept"_qs;
}
void CodeGenerator::recordError(const QQmlJS::SourceLocation &location, const QString &message)
{
- m_logger->logCritical(message, Log_Compiler, location);
+ m_logger->log(message, Log_Compiler, location);
}
void CodeGenerator::recordError(const QV4::CompiledData::Location &location, const QString &message)
{
recordError(QQmlJS::SourceLocation { 0, 0, location.line, location.column }, message);
}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/prototype/codegenerator.h b/tools/qmltc/prototype/codegenerator.h
index 63549ae76e..a18699d23c 100644
--- a/tools/qmltc/prototype/codegenerator.h
+++ b/tools/qmltc/prototype/codegenerator.h
@@ -29,9 +29,8 @@
#ifndef CODEGENERATOR_H
#define CODEGENERATOR_H
-#include "prototype/typeresolver.h"
-#include "prototype/qmlcompiler.h"
-#include "prototype/generatedcodeprimitives.h"
+#include "qmltctyperesolver.h"
+#include "qmltcoutputir.h"
#include "prototype/qml2cppcontext.h"
#include <QtCore/qlist.h>
@@ -40,19 +39,21 @@
#include <QtQml/private/qqmlirbuilder_p.h>
#include <private/qqmljscompiler_p.h>
-#include <private/qqmljstyperesolver_p.h>
#include <variant>
#include <utility>
+QT_BEGIN_NAMESPACE
+
+struct QmltcCompilerInfo;
class CodeGenerator
{
public:
CodeGenerator(const QString &url, QQmlJSLogger *logger, QmlIR::Document *doc,
- const Qmltc::TypeResolver *localResolver);
+ const QmltcTypeResolver *localResolver, const QmltcCompilerInfo *info);
// main function: given compilation options, generates C++ code (implicitly)
- void generate(const Options &options);
+ void generate();
// TODO: this should really be just QQmlJSScope::ConstPtr (and maybe C++
// class name), but bindings are currently not represented in QQmlJSScope,
@@ -63,10 +64,9 @@ private:
QString m_url; // document url
QQmlJSLogger *m_logger = nullptr;
QmlIR::Document *m_doc = nullptr;
- const Qmltc::TypeResolver *m_localTypeResolver = nullptr;
- QStringList m_qmlSource; // QML source code split to lines
+ const QmltcTypeResolver *m_localTypeResolver = nullptr;
- Options m_options = {}; // compilation options
+ const QmltcCompilerInfo *m_info = nullptr;
// convenient object abstraction, laid out as QmlIR::Document.objects
QList<CodeGenObject> m_objects;
@@ -79,13 +79,13 @@ private:
// types ignored by the code generator
QSet<QQmlJSScope::ConstPtr> m_ignoredTypes;
- QQmlJSAotMethod m_urlMethod;
+ QmltcMethod m_urlMethod;
// helper struct used for unique string generation
struct UniqueStringId
{
QString combined;
- UniqueStringId(const QQmlJSAotObject &compiled, const QString &value)
+ UniqueStringId(const QmltcType &compiled, const QString &value)
: combined(compiled.cppType + u"_" + value)
{
Q_ASSERT(!compiled.cppType.isEmpty());
@@ -140,19 +140,18 @@ private:
bool m_isAnonymous = false; // crutch to distinguish QML_ELEMENT from QML_ANONYMOUS
// code compilation functions that produce "compiled" entities
- void
- compileObject(QQmlJSAotObject &current, const CodeGenObject &object,
- std::function<void(QQmlJSAotObject &, const CodeGenObject &)> compileElements);
- void compileObjectElements(QQmlJSAotObject &current, const CodeGenObject &object);
- void compileQQmlComponentElements(QQmlJSAotObject &current, const CodeGenObject &object);
-
- void compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &e);
- void compileProperty(QQmlJSAotObject &current, const QQmlJSMetaProperty &p,
+ void compileObject(QmltcType &current, const CodeGenObject &object,
+ std::function<void(QmltcType &, const CodeGenObject &)> compileElements);
+ void compileObjectElements(QmltcType &current, const CodeGenObject &object);
+ void compileQQmlComponentElements(QmltcType &current, const CodeGenObject &object);
+
+ void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
+ void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
const QQmlJSScope::ConstPtr &owner);
- void compileAlias(QQmlJSAotObject &current, const QQmlJSMetaProperty &alias,
+ void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner);
- void compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMethod &m,
- const QmlIR::Function *f, const CodeGenObject &object);
+ void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m, const QmlIR::Function *f,
+ const CodeGenObject &object);
void compileUrlMethod(); // special case
// helper structure that holds the information necessary for most bindings,
@@ -166,17 +165,17 @@ private:
QString propertyName; // usually empty
bool isValueType = false; // usually false
};
- void compileBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
+ void compileBinding(QmltcType &current, const QmlIR::Binding &binding,
const CodeGenObject &object, const AccessorData &accessor);
// special case (for simplicity)
- void compileScriptBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
+ void compileScriptBinding(QmltcType &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName, const CodeGenObject &object,
const QString &propertyName,
const QQmlJSScope::ConstPtr &propertyType,
const AccessorData &accessor);
// TODO: remove this special case
- void compileScriptBindingOfComponent(QQmlJSAotObject &current, const QmlIR::Object *object,
+ void compileScriptBindingOfComponent(QmltcType &current, const QmlIR::Object *object,
const QQmlJSScope::ConstPtr objectType,
const QmlIR::Binding &binding,
const QString &propertyName);
@@ -186,4 +185,6 @@ private:
void recordError(const QV4::CompiledData::Location &location, const QString &message);
};
+QT_END_NAMESPACE
+
#endif // CODEGENERATOR_H
diff --git a/tools/qmltc/prototype/codegeneratorutil.cpp b/tools/qmltc/prototype/codegeneratorutil.cpp
index 3d7300e0d8..878a1f4b07 100644
--- a/tools/qmltc/prototype/codegeneratorutil.cpp
+++ b/tools/qmltc/prototype/codegeneratorutil.cpp
@@ -33,13 +33,14 @@
#include <tuple>
+QT_BEGIN_NAMESPACE
+
// NB: this variable would behave correctly as long as QML init and QML finalize
// are non-virtual functions
-const QQmlJSAotVariable CodeGeneratorUtility::childrenOffsetVariable = { u"qsizetype"_qs,
- u"QML_choffset"_qs,
- QString() };
+const QmltcVariable CodeGeneratorUtility::childrenOffsetVariable { u"qsizetype"_qs,
+ u"QML_choffset"_qs, QString() };
-const QQmlJSAotVariable CodeGeneratorUtility::compilationUnitVariable = {
+const QmltcVariable CodeGeneratorUtility::compilationUnitVariable {
u"QV4::ExecutableCompilationUnit *"_qs, u"QML_cu"_qs, QString()
};
@@ -141,7 +142,7 @@ QStringList CodeGeneratorUtility::generate_assignToSpecialAlias(
QStringList CodeGeneratorUtility::generate_callExecuteRuntimeFunction(
const QString &url, qsizetype index, const QString &accessor, const QString &returnType,
- const QList<QQmlJSAotVariable> &parameters)
+ const QList<QmltcVariable> &parameters)
{
QStringList code;
code.reserve(12); // should always be enough
@@ -163,7 +164,7 @@ QStringList CodeGeneratorUtility::generate_callExecuteRuntimeFunction(
types << u"QMetaType::fromType<std::decay_t<" + returnType + u">>()";
}
- for (const QQmlJSAotVariable &p : parameters) {
+ 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">>()";
@@ -212,12 +213,12 @@ QStringList CodeGeneratorUtility::generate_createBindingOnProperty(
return code;
}
-QString CodeGeneratorUtility::generate_qOverload(const QList<QQmlJSAotVariable> &params,
+QString CodeGeneratorUtility::generate_qOverload(const QList<QmltcVariable> &params,
const QString &overloaded)
{
QStringList types;
types.reserve(params.size());
- for (const QQmlJSAotVariable &p : params)
+ for (const QmltcVariable &p : params)
types.emplaceBack(p.cppType);
return u"qOverload<" + types.join(u", "_qs) + u">(" + overloaded + u")";
}
@@ -245,3 +246,5 @@ QString CodeGeneratorUtility::generate_setIdValue(const QString &context, qsizet
return context + u"->setIdValue(" + QString::number(index) + idComment + u", " + accessor
+ u")";
}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/prototype/codegeneratorutil.h b/tools/qmltc/prototype/codegeneratorutil.h
index d8b8defac8..4f5518aeed 100644
--- a/tools/qmltc/prototype/codegeneratorutil.h
+++ b/tools/qmltc/prototype/codegeneratorutil.h
@@ -29,7 +29,7 @@
#ifndef CODEGENERATORUTIL_H
#define CODEGENERATORUTIL_H
-#include "prototype/qmlcompiler.h"
+#include "qmltcoutputir.h"
#include <private/qqmljsscope_p.h>
#include <private/qqmljsmetatypes_p.h>
@@ -39,6 +39,8 @@
#include <utility>
+QT_BEGIN_NAMESPACE
+
struct CodeGeneratorUtility
{
// magic variable, necessary for correct handling of object bindings: since
@@ -51,10 +53,10 @@ struct CodeGeneratorUtility
// reference any object in the document by id, which automatically means
// that all ids have to be set up before we get to finalization (and the
// only place for it is init)
- static const QQmlJSAotVariable childrenOffsetVariable;
+ static const QmltcVariable childrenOffsetVariable;
// represents QV4::ExecutableCompilationUnit
- static const QQmlJSAotVariable compilationUnitVariable;
+ static const QmltcVariable compilationUnitVariable;
// helper functions:
static QString toResourcePath(const QString &s)
@@ -82,13 +84,13 @@ struct CodeGeneratorUtility
static QStringList
generate_callExecuteRuntimeFunction(const QString &url, qsizetype index,
const QString &accessor, const QString &returnType,
- const QList<QQmlJSAotVariable> &parameters = {});
+ const QList<QmltcVariable> &parameters = {});
static QStringList
generate_createBindingOnProperty(const QString &unitVarName, const QString &scope,
qsizetype functionIndex, const QString &target,
int propertyIndex, const QQmlJSMetaProperty &p,
int valueTypeIndex, const QString &subTarget);
- static QString generate_qOverload(const QList<QQmlJSAotVariable> &parameters,
+ static QString generate_qOverload(const QList<QmltcVariable> &parameters,
const QString &overloaded);
static QString generate_addressof(const QString &addressed);
static QString generate_getPrivateClass(const QString &accessor, const QQmlJSMetaProperty &p);
@@ -96,4 +98,6 @@ struct CodeGeneratorUtility
const QString &accessor, const QString &idString);
};
+QT_END_NAMESPACE
+
#endif // CODEGENERATORUTIL_H
diff --git a/tools/qmltc/prototype/codegeneratorwriter.cpp b/tools/qmltc/prototype/codegeneratorwriter.cpp
deleted file mode 100644
index d43a94084e..0000000000
--- a/tools/qmltc/prototype/codegeneratorwriter.cpp
+++ /dev/null
@@ -1,476 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 "codegeneratorwriter.h"
-
-#include <private/qqmljsmetatypes_p.h>
-
-#include <QtCore/qfileinfo.h>
-
-#include <utility>
-#include <functional>
-
-static constexpr char16_t newLine[] =
-#ifdef Q_OS_WIN32
- u"\r\n";
-#else
- u"\n";
-#endif
-static constexpr char newLineLatin1[] =
-#ifdef Q_OS_WIN32
- "\r\n";
-#else
- "\n";
-#endif
-
-static QString urlToMacro(const QString &url)
-{
- QFileInfo fi(url);
- return u"Q_QMLTC_" + fi.baseName().toUpper();
-}
-
-static QString getFunctionCategory(const QQmlJSAotMethodBase &compiled)
-{
- QString category;
- switch (compiled.access) {
- case QQmlJSMetaMethod::Private:
- category = u"private"_qs;
- break;
- case QQmlJSMetaMethod::Protected:
- category = u"protected"_qs;
- break;
- case QQmlJSMetaMethod::Public:
- category = u"public"_qs;
- break;
- }
- return category;
-}
-
-static QString getFunctionCategory(const QQmlJSAotMethod &compiled)
-{
- QString category = getFunctionCategory(static_cast<const QQmlJSAotMethodBase &>(compiled));
- switch (compiled.type) {
- case QQmlJSMetaMethod::Signal:
- category = u"signals"_qs;
- break;
- case QQmlJSMetaMethod::Slot:
- category += u" slots"_qs;
- break;
- case QQmlJSMetaMethod::Method:
- break;
- }
- return category;
-}
-
-void CodeGeneratorWriter::writeGlobalHeader(GeneratedCodeUtils &code, const QString &sourceName,
- const QString &hPath, const QString &cppPath,
- const QString &outNamespace,
- const QSet<QString> &requiredCppIncludes)
-{
- Q_UNUSED(newLineLatin1);
-
- Q_UNUSED(cppPath);
- const QString preamble =
- u"// This code is auto-generated by the qmlcompiler tool from the file '" + sourceName
- + u"'" + newLine + u"// WARNING! All changes made in this file will be lost!" + newLine;
- code.appendToHeader(preamble);
- code.appendToImpl(preamble);
- code.appendToHeader(u"// NOTE: This generated API is to be considered implementation detail.");
- code.appendToHeader(
- u"// It may change from version to version and should not be relied upon.");
-
- const QString headerMacro = urlToMacro(sourceName);
- code.appendToHeader(u"#ifndef %1_H"_qs.arg(headerMacro));
- code.appendToHeader(u"#define %1_H"_qs.arg(headerMacro));
-
- code.appendToHeader(u"#include <QtCore/qproperty.h>");
- code.appendToHeader(u"#include <QtCore/qobject.h>");
- code.appendToHeader(u"#include <QtCore/qcoreapplication.h>");
- code.appendToHeader(u"#include <QtQml/qqmlengine.h>");
- code.appendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
- code.appendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
-
- code.appendToHeader(u"#include <private/qqmlengine_p.h>"); // NB: private header
-
- code.appendToHeader(u"#include <QQmlListProperty>"); // required by list properties
-
- // include custom C++ includes required by used types
- code.appendToHeader(u"// BEGIN(custom_cpp_includes)");
- for (const auto &requiredInclude : requiredCppIncludes) {
- code.appendToHeader(u"#include \"" + requiredInclude + u"\"");
- }
- code.appendToHeader(u"// END(custom_cpp_includes)");
-
- code.appendToImpl(u"#include \"" + hPath + u"\""); // include own .h file
- code.appendToImpl(u"#include <private/qqmlcppbinding_p.h>"); // QmltcSupportLib
- code.appendToImpl(u"#include <private/qqmlcpponassignment_p.h>"); // QmltcSupportLib
-
- code.appendToImpl(u"#include <private/qqmlobjectcreator_p.h>"); // createComponent()
- code.appendToImpl(u"#include <private/qqmlcomponent_p.h>"); // QQmlComponentPrivate::get()
-
- code.appendToImpl(u"");
- code.appendToImpl(u"#include <private/qobject_p.h>"); // NB: for private properties
- code.appendToImpl(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
-
- code.appendToImpl(u""); // blank line
- if (!outNamespace.isEmpty()) {
- code.appendToHeader(u""); // blank line
- code.appendToHeader(u"namespace %1 {"_qs.arg(outNamespace));
- code.appendToImpl(u""); // blank line
- code.appendToImpl(u"namespace %1 {"_qs.arg(outNamespace));
- }
-}
-
-void CodeGeneratorWriter::writeGlobalFooter(GeneratedCodeUtils &code, const QString &sourceName,
- const QString &hPath, const QString &cppPath,
- const QString &outNamespace)
-{
- Q_UNUSED(code);
- Q_UNUSED(hPath);
- Q_UNUSED(cppPath);
-
- if (!outNamespace.isEmpty()) {
- code.appendToImpl(u"} // namespace %1"_qs.arg(outNamespace));
- code.appendToImpl(u""); // blank line
- code.appendToHeader(u"} // namespace %1"_qs.arg(outNamespace));
- code.appendToHeader(u""); // blank line
- }
-
- code.appendToHeader(u"#endif // %1_H"_qs.arg(urlToMacro(sourceName)));
- code.appendToHeader(u""); // blank line
-}
-
-static QString classString(const QQmlJSAotObject &compiled)
-{
- QString str = u"class " + compiled.cppType;
- QStringList nonEmptyBaseClasses;
- nonEmptyBaseClasses.reserve(compiled.baseClasses.size());
- std::copy_if(compiled.baseClasses.cbegin(), compiled.baseClasses.cend(),
- std::back_inserter(nonEmptyBaseClasses),
- [](const QString &entry) { return !entry.isEmpty(); });
- if (!nonEmptyBaseClasses.isEmpty())
- str += u" : public " + nonEmptyBaseClasses.join(u", public "_qs);
- return str;
-}
-
-template<typename Predicate>
-static void dumpFunctions(GeneratedCodeUtils &code, const QList<QQmlJSAotMethod> &functions,
- Predicate pred)
-{
- // functions are _ordered_ by access and kind. ordering is important to
- // provide consistent output
- QMap<QString, QList<const QQmlJSAotMethod *>> 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.appendToHeader(it.key() + u":", -1);
- for (const QQmlJSAotMethod *function : qAsConst(it.value()))
- CodeGeneratorWriter::write(code, *function);
- }
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotObject &compiled)
-{
- code.appendToHeader(u""); // just new line
- code.appendToImpl(u""); // just new line
-
- // generate class preamble
- code.appendToHeader(classString(compiled));
- code.appendToHeader(u"{");
- for (const QString &mocLine : qAsConst(compiled.mocCode))
- code.appendToHeader(mocLine, 1);
-
- for (const QString &otherLine : qAsConst(compiled.otherCode))
- code.appendToHeader(otherLine, 1);
-
- GeneratedCodeUtils::MemberNamespaceScope thisObjectScope(code, compiled.cppType);
- Q_UNUSED(thisObjectScope);
- {
- GeneratedCodeUtils::HeaderIndentationScope headerIndentScope(code);
- Q_UNUSED(headerIndentScope);
-
- // 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.appendToHeader(u"// -----------------");
- code.appendToHeader(u"// External C++ API:");
- code.appendToHeader(u"public:", -1);
-
- // 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 (!compiled.ignoreInit && compiled.externalCtor.access == QQmlJSMetaMethod::Public) {
- // TODO: ignoreInit must be eliminated
-
- CodeGeneratorWriter::write(code, compiled.externalCtor);
- }
- // generate dtor
- if (compiled.dtor)
- CodeGeneratorWriter::write(code, *compiled.dtor);
-
- // generate enums
- for (const auto &enumeration : qAsConst(compiled.enums))
- CodeGeneratorWriter::write(code, enumeration);
-
- // generate (visible) functions
- const auto isUserVisibleFunction = [](const QQmlJSAotMethod &function) {
- return function.userVisible;
- };
- dumpFunctions(code, compiled.functions, isUserVisibleFunction);
-
- code.appendToHeader(u"// -----------------");
- code.appendToHeader(u""); // blank line
- code.appendToHeader(u"// Internal functionality (do NOT use it!):");
-
- // below are the hidden parts of the class
-
- // generate (rest of the) ctors
- if (compiled.ignoreInit) { // TODO: this branch should be eliminated
- Q_ASSERT(compiled.baselineCtor.access == QQmlJSMetaMethod::Public);
- code.appendToHeader(u"public:", -1);
- CodeGeneratorWriter::write(code, compiled.baselineCtor);
- } else {
- code.appendToHeader(u"protected:", -1);
- if (compiled.externalCtor.access != QQmlJSMetaMethod::Public) {
- Q_ASSERT(compiled.externalCtor.access == QQmlJSMetaMethod::Protected);
- CodeGeneratorWriter::write(code, compiled.externalCtor);
- }
- CodeGeneratorWriter::write(code, compiled.baselineCtor);
- CodeGeneratorWriter::write(code, compiled.init);
- CodeGeneratorWriter::write(code, compiled.endInit);
- CodeGeneratorWriter::write(code, compiled.completeComponent);
- CodeGeneratorWriter::write(code, compiled.finalizeComponent);
- CodeGeneratorWriter::write(code, compiled.handleOnCompleted);
-
- // code.appendToHeader(u"public:", -1);
- }
-
- // generate child types
- code.appendToHeader(u"// BEGIN(children)");
- for (const auto &child : qAsConst(compiled.children))
- CodeGeneratorWriter::write(code, child);
- code.appendToHeader(u"// END(children)");
-
- // generate functions
- code.appendToHeader(u"// BEGIN(hidden_functions)");
- dumpFunctions(code, compiled.functions, std::not_fn(isUserVisibleFunction));
- code.appendToHeader(u"// END(hidden_functions)");
-
- if (!compiled.variables.isEmpty() || !compiled.properties.isEmpty()) {
- code.appendToHeader(u""); // blank line
- code.appendToHeader(u"protected:", -1);
- }
- // generate variables
- if (!compiled.variables.isEmpty()) {
- code.appendToHeader(u"// BEGIN(variables)");
- for (const auto &variable : qAsConst(compiled.variables))
- CodeGeneratorWriter::write(code, variable);
- code.appendToHeader(u"// END(variables)");
- }
-
- // generate properties
- if (!compiled.properties.isEmpty()) {
- code.appendToHeader(u"// BEGIN(properties)");
- for (const auto &property : qAsConst(compiled.properties))
- CodeGeneratorWriter::write(code, property);
- code.appendToHeader(u"// END(properties)");
- }
- }
-
- code.appendToHeader(u"};");
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotEnum &compiled)
-{
- code.appendToHeader(u"enum " + compiled.cppType + u" {");
- for (qsizetype i = 0; i < compiled.keys.size(); ++i) {
- QString str;
- if (compiled.values.isEmpty()) {
- str += compiled.keys.at(i) + u",";
- } else {
- str += compiled.keys.at(i) + u" = " + compiled.values.at(i) + u",";
- }
- code.appendToHeader(str, 1);
- }
- code.appendToHeader(u"};");
- code.appendToHeader(compiled.ownMocLine);
-}
-
-// NB: property generation is only concerned with property declaration in the header
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotVariable &compiled)
-{
- if (compiled.defaultValue.isEmpty()) {
- code.appendToHeader(compiled.cppType + u" " + compiled.name + u";");
- } else {
- code.appendToHeader(compiled.cppType + u" " + compiled.name + u" = " + compiled.defaultValue
- + u";");
- }
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotProperty &prop)
-{
- Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet
- code.appendToHeader(u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_qs.arg(
- prop.containingClass, prop.cppType, prop.name, prop.signalName));
-}
-
-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 QQmlJSAotMethodBase &m)
-{
- const QString name = m.name;
- const QList<QQmlJSAotVariable> &parameterList = m.parameterList;
- QStringList headerParamList;
- QStringList implParamList;
- for (const QQmlJSAotVariable &variable : parameterList) {
- const QString commonPart = variable.cppType + u" " + variable.name;
- implParamList << commonPart;
- headerParamList << commonPart;
- if (!variable.defaultValue.isEmpty())
- headerParamList.back() += u" = " + variable.defaultValue;
- }
- const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")"
- + prependSpace(m.modifiers.join(u" "));
- const QString implSignature = name + u"(" + implParamList.join(u", "_qs) + u")"
- + prependSpace(m.modifiers.join(u" "));
- return { headerSignature, implSignature };
-}
-
-static QString functionReturnType(const QQmlJSAotMethodBase &m)
-{
- return appendSpace(m.declPreambles.join(u" "_qs)) + m.returnType;
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotMethod &compiled)
-{
- const auto [hSignature, cppSignature] = functionSignatures(compiled);
- // Note: augment return type with preambles in declaration
- code.appendToHeader(functionReturnType(compiled) + u" " + hSignature + u";");
-
- // do not generate method implementation if it is a signal
- const auto methodType = compiled.type;
- if (methodType != QQmlJSMetaMethod::Signal) {
- code.appendToImpl(u""); // just new line
- code.appendToImpl(compiled.returnType);
- code.appendSignatureToImpl(cppSignature);
- code.appendToImpl(u"{");
- {
- GeneratedCodeUtils::ImplIndentationScope indentScope(code);
- Q_UNUSED(indentScope);
- for (const QString &line : qAsConst(compiled.firstLines))
- code.appendToImpl(line);
- for (const QString &line : qAsConst(compiled.body))
- code.appendToImpl(line);
- for (const QString &line : qAsConst(compiled.lastLines))
- code.appendToImpl(line);
- }
- code.appendToImpl(u"}");
- }
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotSpecialMethod &compiled)
-{
- const auto [hSignature, cppSignature] = functionSignatures(compiled);
- const QString returnTypeWithSpace =
- compiled.returnType.isEmpty() ? u""_qs : compiled.returnType + u" ";
-
- code.appendToHeader(returnTypeWithSpace + hSignature + u";");
-
- code.appendToImpl(u""); // just new line
- if (!returnTypeWithSpace.isEmpty())
- code.appendToImpl(returnTypeWithSpace);
- code.appendSignatureToImpl(cppSignature);
- if (!compiled.initializerList.isEmpty()) {
- code.appendToImpl(u":", 1);
- code.appendToImpl(compiled.initializerList.join(u","_qs + newLine + newLine
- + u" "_qs.repeated(code.implIndent + 1)),
- 1);
- }
- code.appendToImpl(u"{");
- {
- GeneratedCodeUtils::ImplIndentationScope indentScope(code);
- Q_UNUSED(indentScope);
- for (const QString &line : qAsConst(compiled.firstLines))
- code.appendToImpl(line);
- for (const QString &line : qAsConst(compiled.body))
- code.appendToImpl(line);
- for (const QString &line : qAsConst(compiled.lastLines))
- code.appendToImpl(line);
- }
- code.appendToImpl(u"}");
-}
-
-void CodeGeneratorWriter::writeUrl(GeneratedCodeUtils &code, const QQmlJSAotMethod &urlMethod)
-{
- const auto [hSignature, _] = functionSignatures(urlMethod);
- Q_UNUSED(_);
- Q_ASSERT(!urlMethod.returnType.isEmpty());
- code.appendToImpl(functionReturnType(urlMethod) + hSignature);
- code.appendToImpl(u"{");
- {
- GeneratedCodeUtils::ImplIndentationScope indentScope(code);
- Q_UNUSED(indentScope);
- Q_ASSERT(urlMethod.firstLines.isEmpty() && urlMethod.lastLines.isEmpty());
- for (const QString &line : qAsConst(urlMethod.body))
- code.appendToImpl(line);
- }
- code.appendToImpl(u"}");
-}
-
-void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSProgram &compiled)
-{
- writeGlobalHeader(code, compiled.url, compiled.hPath, compiled.cppPath, compiled.outNamespace,
- compiled.includes);
-
- code.appendToImpl(u""); // just new line
- writeUrl(code, compiled.urlMethod);
-
- // forward declare objects before writing them
- for (const QQmlJSAotObject &compiled : qAsConst(compiled.compiledObjects))
- code.appendToHeader(u"class " + compiled.cppType + u";");
-
- // write all the objects
- for (const QQmlJSAotObject &compiled : qAsConst(compiled.compiledObjects))
- write(code, compiled);
-
- writeGlobalFooter(code, compiled.url, compiled.hPath, compiled.cppPath, compiled.outNamespace);
-}
diff --git a/tools/qmltc/prototype/codegeneratorwriter.h b/tools/qmltc/prototype/codegeneratorwriter.h
deleted file mode 100644
index e03a2123ac..0000000000
--- a/tools/qmltc/prototype/codegeneratorwriter.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 CODEGENERATORWRITER_H
-#define CODEGENERATORWRITER_H
-
-#include "generatedcodeprimitives.h"
-#include "qmlcompiler.h"
-
-// writes compiled code into the GeneratedCode structure
-struct CodeGeneratorWriter
-{
- static void writeGlobalHeader(GeneratedCodeUtils &code, const QString &sourceName,
- const QString &hPath, const QString &cppPath,
- const QString &outNamespace,
- const QSet<QString> &requiredCppIncludes);
- static void writeGlobalFooter(GeneratedCodeUtils &code, const QString &sourceName,
- const QString &hPath, const QString &cppPath,
- const QString &outNamespace);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotObject &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotEnum &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotVariable &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotProperty &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotMethod &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSAotSpecialMethod &compiled);
- static void write(GeneratedCodeUtils &code, const QQmlJSProgram &compiled);
-
-private:
- static void writeUrl(GeneratedCodeUtils &code, const QQmlJSAotMethod &urlMethod);
-};
-
-#endif // CODEGENERATORWRITER_H
diff --git a/tools/qmltc/prototype/generatedcodeprimitives.h b/tools/qmltc/prototype/generatedcodeprimitives.h
deleted file mode 100644
index 46d7b1461f..0000000000
--- a/tools/qmltc/prototype/generatedcodeprimitives.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 GENERATEDCODEPRIMITIVES_H
-#define GENERATEDCODEPRIMITIVES_H
-
-#include <QtCore/qstring.h>
-#include <QtCore/qstack.h>
-
-// holds generated code for header and implementation files
-struct GeneratedCode
-{
- QString header;
- QString implementation;
-};
-
-// utility class that provides pretty-printing of the generated code into the
-// GeneratedCode buffer
-struct GeneratedCodeUtils
-{
- GeneratedCode &m_code; // buffer
-
- QStack<QString> memberNamespaceStack; // member names scopes e.g. MyClass::MySubclass::
- int headerIndent = 0; // header indentation level
- int implIndent = 0; // implementation indentation level
-
- GeneratedCodeUtils(GeneratedCode &code) : m_code(code) { }
-
- // manages current scope of the generated code, which is necessary for
- // implementation file generation. Example:
- // class MyClass { MyClass(); }; - in header
- // MyClass::MyClass() {} - in implementation file
- // MemberNamespaceScope exists to be able to record and use "MyClass::"
- struct MemberNamespaceScope
- {
- GeneratedCodeUtils &m_code;
- MemberNamespaceScope(GeneratedCodeUtils &code, const QString &str) : m_code(code)
- {
- m_code.memberNamespaceStack.push(str);
- }
- ~MemberNamespaceScope() { m_code.memberNamespaceStack.pop(); }
- };
-
- // manages current indentation scope: upon creation, increases current
- // scope, which is decreased back upon deletion. this is used by append*
- // functions that work with GeneratedCode::header to correctly indent the
- // input
- struct HeaderIndentationScope
- {
- GeneratedCodeUtils &m_code;
- HeaderIndentationScope(GeneratedCodeUtils &code) : m_code(code) { ++m_code.headerIndent; }
- ~HeaderIndentationScope() { --m_code.headerIndent; }
- };
-
- // manages current indentation scope: upon creation, increases current
- // scope, which is decreased back upon deletion. this is used by append*
- // functions that work with GeneratedCode::implementation to correctly
- // indent the input
- struct ImplIndentationScope
- {
- GeneratedCodeUtils &m_code;
- ImplIndentationScope(GeneratedCodeUtils &code) : m_code(code) { ++m_code.implIndent; }
- ~ImplIndentationScope() { --m_code.implIndent; }
- };
-
- // appends string \a what with extra indentation \a extraIndent to current
- // GeneratedCode::header string
- template<typename String>
- void appendToHeader(const String &what, int extraIndent = 0)
- {
- constexpr char16_t newLine[] = u"\n";
- m_code.header += QString((headerIndent + extraIndent) * 4, u' ') + what + newLine;
- }
-
- // appends string \a what with extra indentation \a extraIndent to current
- // GeneratedCode::implementation string
- template<typename String>
- void appendToImpl(const String &what, int extraIndent = 0)
- {
- constexpr char16_t newLine[] = u"\n";
- m_code.implementation += QString((implIndent + extraIndent) * 4, u' ') + what + newLine;
- }
-
- // appends string \a what with extra indentation \a extraIndent to current
- // GeneratedCode::implementation string. this is a special case function
- // that expects \a what to be a function signature as \a what is prepended
- // with member scope related text. for example, string "foo()" is converted
- // to string "MyClass::foo()" before append
- template<typename String>
- void appendSignatureToImpl(const String &what, int extraIndent = 0)
- {
- constexpr char16_t newLine[] = u"\n";
- QString signatureScope;
- for (const auto &subScope : memberNamespaceStack)
- signatureScope += subScope + u"::";
- m_code.implementation +=
- signatureScope + QString((implIndent + extraIndent) * 4, u' ') + what + newLine;
- }
-};
-
-#endif // GENERATEDCODEPRIMITIVES_H
diff --git a/tools/qmltc/prototype/qml2cppcontext.h b/tools/qmltc/prototype/qml2cppcontext.h
index 56bb5a12db..3c17a632df 100644
--- a/tools/qmltc/prototype/qml2cppcontext.h
+++ b/tools/qmltc/prototype/qml2cppcontext.h
@@ -29,8 +29,7 @@
#ifndef QML2CPPCONTEXT_H
#define QML2CPPCONTEXT_H
-#include "prototype/qmlcompiler.h"
-#include "prototype/typeresolver.h"
+#include "qmltctyperesolver.h"
#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtQml/private/qqmlirbuilder_p.h>
@@ -41,10 +40,12 @@
#include <variant>
#include <functional>
+QT_BEGIN_NAMESPACE
+
struct Qml2CppContext
{
const QmlIR::Document *document = nullptr;
- const Qmltc::TypeResolver *typeResolver = nullptr;
+ const QmltcTypeResolver *typeResolver = nullptr;
QString documentUrl;
QQmlJSLogger *logger = nullptr;
const QHash<QQmlJSScope::ConstPtr, qsizetype> *typeIndices = nullptr; // TODO: remove this?
@@ -52,7 +53,7 @@ struct Qml2CppContext
void recordError(const QQmlJS::SourceLocation &location, const QString &message) const
{
Q_ASSERT(logger);
- logger->logCritical(message, Log_Compiler, location);
+ logger->log(message, Log_Compiler, location);
}
void recordError(const QV4::CompiledData::Location &location, const QString &message) const
@@ -81,7 +82,7 @@ class Qml2CppCompilerPassExecutor
QList<Qml2CppCompilerPass> m_passes;
public:
- Qml2CppCompilerPassExecutor(const QmlIR::Document *doc, const Qmltc::TypeResolver *resolver,
+ Qml2CppCompilerPassExecutor(const QmlIR::Document *doc, const QmltcTypeResolver *resolver,
const QString &url, QList<Qml2CppObject> &objects,
const QHash<QQmlJSScope::ConstPtr, qsizetype> &typeIndices)
: m_context { doc, resolver, url, nullptr, &typeIndices }, m_objects { objects }
@@ -105,4 +106,6 @@ public:
}
};
+QT_END_NAMESPACE
+
#endif // QML2CPPCONTEXT_H
diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp
index 586cc85754..8372d397d0 100644
--- a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp
+++ b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp
@@ -35,6 +35,8 @@
#include <algorithm>
+QT_BEGIN_NAMESPACE
+
static QString const cppKeywords[] = {
u"alignas"_qs,
u"alignof"_qs,
@@ -534,7 +536,7 @@ static void setupQmlCppType(const Qml2CppContext &context, const QQmlJSScope::Pt
context.recordError(type->sourceLocation(), u"QML type has unknown file path"_qs);
return;
}
- if (!type->fileName().isEmpty()) // consider this one to be already set up
+ if (type->filePath().endsWith(u".h")) // consider this one to be already set up
return;
if (!filePath.endsWith(u".qml"_qs)) {
context.recordError(type->sourceLocation(),
@@ -545,7 +547,7 @@ static void setupQmlCppType(const Qml2CppContext &context, const QQmlJSScope::Pt
// TODO: this does not cover QT_QMLTC_FILE_BASENAME renaming
if (filePath != context.documentUrl) {
// this file name will be discovered during findCppIncludes
- type->setFileName(QFileInfo(filePath).baseName().toLower() + u".h"_qs);
+ type->setFilePath(QFileInfo(filePath).baseName().toLower() + u".h"_qs);
}
const auto properties = type->ownProperties();
@@ -786,7 +788,7 @@ static void addFirstCppIncludeFromType(QSet<QString> &cppIncludes,
auto t = QQmlJSScope::nonCompositeBaseType(type);
if (!t)
return;
- if (QString includeFile = t->fileName(); !includeFile.isEmpty())
+ if (QString includeFile = t->filePath(); includeFile.endsWith(u".h"))
cppIncludes.insert(includeFile);
}
@@ -816,7 +818,7 @@ static void populateCppIncludes(QSet<QString> &cppIncludes, const QQmlJSScope::C
for (auto t = type; t; t = t->baseType()) {
// NB: Composite types might have include files - this is custom qmltc
// logic for local imports
- if (QString includeFile = t->fileName(); !includeFile.isEmpty())
+ if (QString includeFile = t->filePath(); includeFile.endsWith(u".h"))
cppIncludes.insert(includeFile);
// look in property types
@@ -824,8 +826,10 @@ static void populateCppIncludes(QSet<QString> &cppIncludes, const QQmlJSScope::C
for (const QQmlJSMetaProperty &p : properties) {
addFirstCppIncludeFromType(cppIncludes, p.type());
- if (p.isPrivate()) {
- const QString ownersInclude = QQmlJSScope::nonCompositeBaseType(t)->fileName();
+ const auto baseType = QQmlJSScope::nonCompositeBaseType(t);
+
+ if (p.isPrivate() && baseType->filePath().endsWith(u".h")) {
+ const QString ownersInclude = baseType->filePath();
QString privateInclude = constructPrivateInclude(ownersInclude);
if (!privateInclude.isEmpty())
cppIncludes.insert(std::move(privateInclude));
@@ -1079,3 +1083,43 @@ QSet<QQmlJSScope::ConstPtr> collectIgnoredTypes(const Qml2CppContext &context,
return ignored;
}
+
+static void setDeferred(const Qml2CppContext &context, qsizetype objectIndex,
+ QList<Qml2CppObject> &objects)
+{
+ Q_UNUSED(objects);
+
+ Qml2CppObject &o = objects[objectIndex];
+
+ // c.f. QQmlDeferredAndCustomParserBindingScanner::scanObject()
+ if (o.irObject->flags & QV4::CompiledData::Object::IsComponent) {
+ // unlike QmlIR compiler, qmltc should not care about anything within a
+ // component (let the QQmlComponent wrapper - at runtime anyway - take
+ // care of this type instead)
+ return;
+ }
+
+ const auto setRecursive = [&](QmlIR::Binding &binding) {
+ if (binding.type >= QmlIR::Binding::Type_Object)
+ setDeferred(context, binding.value.objectIndex, objects); // Note: recursive call here!
+
+ const QString propName = findPropertyName(context, o.type, binding);
+ Q_ASSERT(!propName.isEmpty());
+
+ if (o.type->isNameDeferred(propName)) {
+ binding.flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ o.irObject->flags |= QV4::CompiledData::Object::HasDeferredBindings;
+ }
+ };
+
+ std::for_each(o.irObject->bindingsBegin(), o.irObject->bindingsEnd(), setRecursive);
+}
+
+void setDeferredBindings(const Qml2CppContext &context, QList<Qml2CppObject> &objects)
+{
+ // as we do not support InlineComponents just yet, we can shortcut the logic
+ // here to only work with root object
+ setDeferred(context, 0, objects);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.h b/tools/qmltc/prototype/qml2cppdefaultpasses.h
index 29e5acf985..6a1d0ca124 100644
--- a/tools/qmltc/prototype/qml2cppdefaultpasses.h
+++ b/tools/qmltc/prototype/qml2cppdefaultpasses.h
@@ -31,6 +31,8 @@
#include "prototype/qml2cppcontext.h"
+QT_BEGIN_NAMESPACE
+
// verifies that each object, property (and etc.) has valid
// QQmlJSScope::ConstPtr associated with it
void verifyTypes(const Qml2CppContext &context, QList<Qml2CppObject> &objects);
@@ -86,4 +88,8 @@ findImmediateParents(const Qml2CppContext &context, QList<Qml2CppObject> &object
QSet<QQmlJSScope::ConstPtr> collectIgnoredTypes(const Qml2CppContext &context,
QList<Qml2CppObject> &objects);
+void setDeferredBindings(const Qml2CppContext &context, QList<Qml2CppObject> &objects);
+
+QT_END_NAMESPACE
+
#endif // QML2CPPPASSES_H
diff --git a/tools/qmltc/prototype/qml2cpppropertyutils.h b/tools/qmltc/prototype/qml2cpppropertyutils.h
index 4b3e513b90..31f3f73297 100644
--- a/tools/qmltc/prototype/qml2cpppropertyutils.h
+++ b/tools/qmltc/prototype/qml2cpppropertyutils.h
@@ -31,6 +31,8 @@
#include <private/qqmljsmetatypes_p.h>
+QT_BEGIN_NAMESPACE
+
inline QString getUnderlyingType(const QQmlJSMetaProperty &p)
{
QString underlyingType = p.type()->internalName();
@@ -64,4 +66,6 @@ struct Qml2CppPropertyData
QString notify;
};
+QT_END_NAMESPACE
+
#endif // QML2CPPPROPERTYUTILS_H
diff --git a/tools/qmltc/prototype/qmlcompiler.h b/tools/qmltc/prototype/qmlcompiler.h
deleted file mode 100644
index 3c770612cc..0000000000
--- a/tools/qmltc/prototype/qmlcompiler.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 QMLCOMPILER_H
-#define QMLCOMPILER_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/qstring.h>
-#include <QtCore/qstringlist.h>
-
-#include <private/qqmljscompiler_p.h>
-#include <private/qqmljsmetatypes_p.h>
-
-struct Options
-{
- QString outputCppFile;
- QString outputHFile;
- QString moduleUri;
- QString resourcePath;
- QString outNamespace;
- bool debugGenerateLineDirective = false;
-};
-
-// TODO: rename the classes into Qmltc* pattern
-
-// Below are the classes that represent a compiled QML types in a string data
-// form. These classes should be used to generate C++ code.
-
-// Represents QML->C++ compiled enumeration type
-struct QQmlJSAotEnum
-{
- QString cppType; // C++ type of enum
- QStringList keys; // enumerator
- QStringList values; // enumerator value
- QString ownMocLine; // special MOC line that follows enum declaration
-
- QQmlJSAotEnum() = default;
- QQmlJSAotEnum(const QString &t, const QStringList &ks, const QStringList &vs, const QString &l)
- : cppType(t), keys(ks), values(vs), ownMocLine(l)
- {
- }
-};
-
-// Represents C++ member variable
-struct QQmlJSAotVariable
-{
- QString cppType; // C++ type of a variable
- QString name; // variable name
- QString defaultValue; // optional default value
-
- QQmlJSAotVariable() = default;
- QQmlJSAotVariable(const QString &t, const QString &n, const QString &v)
- : cppType(t), name(n), defaultValue(v)
- {
- }
-};
-
-struct QQmlJSAotProperty : QQmlJSAotVariable
-{
- QString containingClass;
- QString signalName;
-
- QQmlJSAotProperty() = default;
- QQmlJSAotProperty(const QString t, const QString &n, const QString &c, const QString &s)
- : QQmlJSAotVariable(t, n, QString()), containingClass(c), signalName(s)
- {
- }
-};
-
-struct QQmlJSAotMethodBase
-{
- QString returnType; // C++ return type
- QString name; // C++ function name
- QList<QQmlJSAotVariable> parameterList; // C++ function parameter list
- QStringList body; // C++ code of function body by line
- QStringList declPreambles; // e.g. "static" keyword
- QStringList modifiers; // e.g. cv-qualifiers, ref-qualifier, noexcept, attributes
-
- // TODO: these are only needed for Component.onCompleted -- any better way?
- QStringList firstLines; // C++ to run at the very beginning of a function
- QStringList lastLines; // C++ to run at the very end of a function
-
- QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
-};
-
-// Represents QML->C++ compiled member function
-struct QQmlJSAotMethod : QQmlJSAotMethodBase
-{
- QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
- bool userVisible = false; // tells if a function is prioritized during the output generation
-};
-
-// Represents C++ special member function
-struct QQmlJSAotSpecialMethod : QQmlJSAotMethodBase
-{
- QStringList initializerList; // C++ ctor initializer list
-};
-
-// Represents QML->C++ compiled class type that is used for C++ code generation
-struct QQmlJSAotObject
-{
- QString cppType; // C++ class name of the QML object
- QStringList baseClasses; // C++ class names of base classes
- // TODO: also add "creation string"?
- QStringList mocCode;
- QStringList otherCode; // code that doesn't fit any category, e.g. friend declarations
-
- // TODO: does it really need to be QHash and not QList?
-
- // member types: enumerations and child types
- QList<QQmlJSAotEnum> enums;
- QList<QQmlJSAotObject> children; // these are pretty much always empty
- // special member functions
- QQmlJSAotSpecialMethod baselineCtor = {}; // does primary initialization
- QQmlJSAotMethod init = {}; // begins secondary initialization
- QQmlJSAotMethod endInit = {}; // ends initialization (with binding setup)
- QQmlJSAotMethod completeComponent = {}; // calls componentComplete()
- QQmlJSAotMethod finalizeComponent = {}; // invokes finalizer callbacks
- QQmlJSAotMethod handleOnCompleted = {}; // calls Component.onCompleted
- QQmlJSAotSpecialMethod externalCtor = {}; // calls baselineCtor, calls init
- std::optional<QQmlJSAotSpecialMethod> dtor = {};
- // member functions: methods, signals and slots
- QList<QQmlJSAotMethod> functions;
- // member variables
- QList<QQmlJSAotVariable> variables;
- // member properties
- QList<QQmlJSAotProperty> properties;
-
- // TODO: only needed for binding callables - should be revisited
- bool ignoreInit = false; // specifies whether init and externalCtor should be ignored
-};
-
-struct QQmlJSProgram
-{
- QList<QQmlJSAotObject> compiledObjects;
- QQmlJSAotMethod urlMethod;
- QString url;
- QString hPath;
- QString cppPath;
- QString outNamespace;
- QSet<QString> includes;
-};
-
-#endif // QMLCOMPILER_H
diff --git a/tools/qmltc/prototype/typeresolver.h b/tools/qmltc/prototype/typeresolver.h
deleted file mode 100644
index a382c4c07a..0000000000
--- a/tools/qmltc/prototype/typeresolver.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 TYPERESOLVER_H
-#define TYPERESOLVER_H
-
-#include "prototype/visitor.h"
-
-#include <private/qqmljsscope_p.h>
-#include <private/qqmljsast_p.h>
-#include <private/qqmlirbuilder_p.h>
-#include <private/qqmljstyperesolver_p.h>
-
-namespace Qmltc {
-class TypeResolver : public QQmlJSTypeResolver
-{
-public:
- TypeResolver(QQmlJSImporter *importer);
-
- void init(Visitor &visitor, QQmlJS::AST::Node *program);
-
- // TODO: this shouldn't be exposed. instead, all the custom passes on
- // QQmlJSScope types must happen inside Visitor
- QQmlJSScope::Ptr root() const { return m_root; }
-
- 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;
-};
-}
-
-#endif // TYPERESOLVER_H
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index 6c2c018833..67e3c7f06b 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -74,6 +74,20 @@ static QString getFunctionCategory(const QmltcMethod &method)
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;
@@ -89,11 +103,18 @@ static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &met
headerParamList.back() += u" = " + variable.defaultValue;
}
- const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")";
- const QString cppSignature = name + u"(" + cppParamList.join(u", "_qs) + u")";
+ const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")"
+ + prependSpace(method.modifiers.join(u" "));
+ const QString cppSignature = name + u"(" + cppParamList.join(u", "_qs) + u")"
+ + prependSpace(method.modifiers.join(u" "));
return { headerSignature, cppSignature };
}
+static QString functionReturnType(const QmltcMethod &m)
+{
+ return appendSpace(m.declarationPrefixes.join(u" "_qs)) + m.returnType;
+}
+
void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &hPath, const QString &cppPath,
const QString &outNamespace,
@@ -129,14 +150,18 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
for (const auto &requiredInclude : requiredCppIncludes)
code.rawAppendToHeader(u"#include \"" + requiredInclude + u"\"");
code.rawAppendToHeader(u"// END(custom_cpp_includes)");
- code.rawAppendToHeader(u"// qmltc support library:");
- code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>");
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.rawAppendToCpp(u""); // blank line
+ 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/qqmlglobal_p.h>"); // QQml_setParent_noEvent()
+ code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
code.rawAppendToCpp(u""); // blank line
code.rawAppendToCpp(u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE");
@@ -187,10 +212,6 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
{
writeGlobalHeader(code, program.url, program.hPath, program.cppPath, program.outNamespace,
program.includes);
- // TODO: keep the "NOT IMPLEMENTED" as long as we don't actually compile
- // useful code
- code.rawAppendToHeader(u"/* QMLTC: NOT IMPLEMENTED */");
- code.rawAppendToCpp(u"/* QMLTC: NOT IMPLEMENTED */");
// url method comes first
writeUrl(code, program.urlMethod);
@@ -207,12 +228,36 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
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 : qAsConst(it.value()))
+ QmltcCodeWriter::write(code, *function);
+ }
+}
+
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
{
const auto constructClassString = [&]() {
QString str = u"class " + type.cppType;
- if (!type.baseClasses.isEmpty())
- str += u" : public " + type.baseClasses.join(u", public "_qs);
+ 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 "_qs);
return str;
};
@@ -233,42 +278,70 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
Q_UNUSED(headerIndent);
- // special member functions
- code.rawAppendToHeader(u"protected:", -1);
- write(code, type.basicCtor);
- write(code, type.init);
- write(code, type.finalize);
- // NB: externalCtor might not be public when the type is QML singleton
- code.rawAppendToHeader(getFunctionCategory(type.fullCtor) + u":", -1);
- write(code, type.fullCtor);
+ // 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
- // enums
- if (!type.enums.isEmpty()) {
- code.rawAppendToHeader(u""); // blank line
- code.rawAppendToHeader(u"public:", -1);
+ code.rawAppendToHeader(u"/* ----------------- */");
+ code.rawAppendToHeader(u"/* External C++ API */");
+ code.rawAppendToHeader(u"public:", -1);
+
+ // 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);
}
+
+ // dtor
+ if (type.dtor)
+ QmltcCodeWriter::write(code, *type.dtor);
+
+ // enums
for (const auto &enumeration : qAsConst(type.enums))
- write(code, enumeration);
+ QmltcCodeWriter::write(code, enumeration);
- // child types
- if (!type.children.isEmpty())
- code.rawAppendToHeader(u""); // blank line
- for (const auto &child : qAsConst(type.children))
- write(code, child);
+ // visible functions
+ const auto isUserVisibleFunction = [](const QmltcMethod &function) {
+ return function.userVisible;
+ };
+ dumpFunctions(code, type.functions, isUserVisibleFunction);
- // functions (special case due to functions/signals/slots, etc.)
- QHash<QString, QList<const QmltcMethod *>> functionsByCategory;
- for (const auto &function : qAsConst(type.functions))
- functionsByCategory[getFunctionCategory(function)].append(&function);
+ code.rawAppendToHeader(u"/* ----------------- */");
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"/* Internal functionality (do NOT use it!) */");
- if (!functionsByCategory.isEmpty())
- code.rawAppendToHeader(u""); // blank line
- for (auto it = functionsByCategory.cbegin(); it != functionsByCategory.cend(); ++it) {
- code.rawAppendToHeader(it.key() + u":", -1);
- for (const QmltcMethod *function : qAsConst(it.value()))
- write(code, *function);
+ // 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.completeComponent);
+ QmltcCodeWriter::write(code, type.finalizeComponent);
+ QmltcCodeWriter::write(code, type.handleOnCompleted);
+
+ // code.rawAppendToHeader(u"public:", -1);
}
+ // children
+ for (const auto &child : qAsConst(type.children))
+ QmltcCodeWriter::write(code, child);
+
+ // (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
@@ -314,10 +387,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
const auto [hSignature, cppSignature] = functionSignatures(method);
// Note: augment return type with preambles in declaration
- QString prefix = method.declarationPrefixes.join(u' ');
- if (!prefix.isEmpty())
- prefix.append(u' ');
- code.rawAppendToHeader(prefix + method.returnType + u" " + hSignature + u";");
+ code.rawAppendToHeader(functionReturnType(method) + u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = method.type;
@@ -329,39 +399,65 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
+ for (const QString &line : qAsConst(method.firstLines))
+ code.rawAppendToCpp(line);
for (const QString &line : qAsConst(method.body))
code.rawAppendToCpp(line);
+ for (const QString &line : qAsConst(method.lastLines))
+ code.rawAppendToCpp(line);
}
code.rawAppendToCpp(u"}");
}
}
-void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
+template<typename WriteInitialization>
+static void writeSpecialMethod(QmltcOutputWrapper &code, const QmltcMethodBase &specialMethod,
+ WriteInitialization writeInit)
{
- const auto [hSignature, cppSignature] = functionSignatures(ctor);
- QString prefix = ctor.declarationPrefixes.join(u' ');
- if (!prefix.isEmpty())
- prefix.append(u' ');
- code.rawAppendToHeader(prefix + hSignature + u";");
+ const auto [hSignature, cppSignature] = functionSignatures(specialMethod);
+ code.rawAppendToHeader(hSignature + u";");
code.rawAppendToCpp(u""); // blank line
code.rawAppendSignatureToCpp(cppSignature);
- 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" "_qs.repeated(code.cppIndent + 1)), 1);
- }
+
+ writeInit(specialMethod);
+
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
- for (const QString &line : qAsConst(ctor.body))
+ for (const QString &line : qAsConst(specialMethod.firstLines))
+ code.rawAppendToCpp(line);
+ for (const QString &line : qAsConst(specialMethod.body))
+ code.rawAppendToCpp(line);
+ for (const QString &line : qAsConst(specialMethod.lastLines))
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" "_qs.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""_qs : u" = " + var.defaultValue;
@@ -370,7 +466,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProperty &prop)
{
- Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet
+ 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)"_qs.arg(
prop.containingClass, prop.cppType, prop.name, prop.signalName));
}
@@ -382,14 +478,12 @@ void QmltcCodeWriter::writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlM
const auto [hSignature, _] = functionSignatures(urlMethod);
Q_UNUSED(_);
// Note: augment return type with preambles in declaration
- QString prefix = urlMethod.declarationPrefixes.join(u' ');
- if (!prefix.isEmpty())
- prefix.append(u' ');
- code.rawAppendToCpp(prefix + urlMethod.returnType + u" " + hSignature);
+ code.rawAppendToCpp(functionReturnType(urlMethod) + hSignature);
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
+ Q_ASSERT(urlMethod.firstLines.isEmpty() && urlMethod.lastLines.isEmpty());
for (const QString &line : qAsConst(urlMethod.body))
code.rawAppendToCpp(line);
}
diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h
index 1d1e023d43..824a3f97db 100644
--- a/tools/qmltc/qmltccodewriter.h
+++ b/tools/qmltc/qmltccodewriter.h
@@ -49,9 +49,11 @@ struct QmltcCodeWriter
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);
+private:
static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
};
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 6510a6d51d..2b69a81b85 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -115,7 +115,7 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
current.baseClasses = { baseClass };
if (!documentRoot) {
- // make document root a friend to allow it to access init and finalize
+ // make document root a friend to allow it to access init and endInit
current.otherCode << u"friend class %1;"_qs.arg(rootType->internalName());
} else {
// make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
@@ -147,68 +147,70 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
};
// add special member functions
- current.basicCtor.access = QQmlJSMetaMethod::Protected;
+ current.baselineCtor.access = QQmlJSMetaMethod::Protected;
current.init.access = QQmlJSMetaMethod::Protected;
- current.finalize.access = QQmlJSMetaMethod::Protected;
- current.fullCtor.access = QQmlJSMetaMethod::Public;
+ current.endInit.access = QQmlJSMetaMethod::Protected;
+ current.externalCtor.access = QQmlJSMetaMethod::Public;
- current.basicCtor.name = current.cppType;
- current.fullCtor.name = current.cppType;
+ current.baselineCtor.name = current.cppType;
+ current.externalCtor.name = current.cppType;
current.init.name = u"qmltc_init"_qs;
current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_qs;
- current.finalize.name = u"qmltc_finalize"_qs;
- current.finalize.returnType = u"void"_qs;
+ current.endInit.name = u"qmltc_finalize"_qs;
+ current.endInit.returnType = u"void"_qs;
QmltcVariable creator(u"QQmltcObjectCreationHelper*"_qs, u"creator"_qs);
QmltcVariable engine(u"QQmlEngine*"_qs, u"engine"_qs);
QmltcVariable parent(u"QObject*"_qs, u"parent"_qs, u"nullptr"_qs);
- current.basicCtor.parameterList = { parent };
+ current.baselineCtor.parameterList = { parent };
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_qs, u"parentContext"_qs);
QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs);
if (documentRoot) {
- current.fullCtor.parameterList = { engine, parent };
+ current.externalCtor.parameterList = { engine, parent };
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
- current.finalize.parameterList = { creator, engine, finalizeFlag };
+ current.endInit.parameterList = { creator, engine, finalizeFlag };
} else {
- current.fullCtor.parameterList = { creator, engine, parent };
+ current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
- current.finalize.parameterList = { creator, engine };
+ current.endInit.parameterList = { creator, engine };
}
- current.fullCtor.initializerList = { current.basicCtor.name + u"(" + parent.name + u")" };
+ current.externalCtor.initializerList = { current.baselineCtor.name + u"(" + parent.name
+ + u")" };
if (baseTypeIsCompiledQml) {
// call parent's (QML type's) basic ctor from this. that one will take
// care about QObject::setParent()
- current.basicCtor.initializerList = { baseClass + u"(" + parent.name + u")" };
+ 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.basicCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
+ current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
}
QmltcCodeGenerator generator { rootType };
// compilation stub:
- current.fullCtor.body << u"Q_UNUSED(engine);"_qs;
- current.finalize.body << u"Q_UNUSED(engine);"_qs;
- current.finalize.body << u"Q_UNUSED(creator);"_qs;
+ current.externalCtor.body << u"Q_UNUSED(engine);"_qs;
+ current.endInit.body << u"Q_UNUSED(engine);"_qs;
+ current.endInit.body << u"Q_UNUSED(creator);"_qs;
if (documentRoot) {
- current.fullCtor.body << u"// document root:"_qs;
+ current.externalCtor.body << u"// document root:"_qs;
// if it's document root, we want to create our QQmltcObjectCreationBase
// that would store all the created objects
- current.fullCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_qs.arg(
+ current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_qs.arg(
type->internalName());
- current.fullCtor.body << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs;
+ current.externalCtor.body
+ << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs;
// now call init
- current.fullCtor.body << current.init.name
+ current.externalCtor.body << current.init.name
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
- u"finalize */ true);";
+ u"endInit */ true);";
- current.finalize.body << u"Q_UNUSED(canFinalize);"_qs;
+ current.endInit.body << u"Q_UNUSED(canFinalize);"_qs;
} else {
- current.fullCtor.body << u"// not document root:"_qs;
+ current.externalCtor.body << u"// not document root:"_qs;
// just call init, we don't do any setup here otherwise
- current.fullCtor.body << current.init.name
+ current.externalCtor.body << current.init.name
+ u"(creator, engine, QQmlData::get(parent)->outerContext);";
}
@@ -353,9 +355,9 @@ void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty
const QString storageName = variableName + u"_storage";
current.variables.emplaceBack(u"QList<" + p.type()->internalName() + u" *>", storageName,
QString());
- current.basicCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
- + u"(this, std::addressof(" + storageName
- + u")))");
+ current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
+ + u"(this, std::addressof(" + storageName
+ + u")))");
}
// along with property, also add relevant moc code, so that we can use the
@@ -440,18 +442,18 @@ void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyB
switch (binding.bindingType()) {
case QQmlJSMetaPropertyBinding::BoolLiteral: {
- const bool value = binding.literalValue().toBool();
+ const bool value = binding.boolValue();
generator.generate_assignToProperty(current, type, p, value ? u"true"_qs : u"false"_qs,
accessor.name);
break;
}
case QQmlJSMetaPropertyBinding::NumberLiteral: {
- const QString value = QString::number(binding.literalValue().toDouble());
+ const QString value = QString::number(binding.numberValue());
generator.generate_assignToProperty(current, type, p, value, accessor.name);
break;
}
case QQmlJSMetaPropertyBinding::StringLiteral: {
- const QString value = binding.literalValue().toString();
+ const QString value = binding.stringValue();
generator.generate_assignToProperty(current, type, p, QQmlJSUtils::toLiteral(value),
accessor.name);
break;
@@ -486,8 +488,8 @@ void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyB
break;
}
default: {
- m_logger->logWarning(u"Binding type is not supported (yet)"_qs, Log_Compiler,
- binding.sourceLocation());
+ m_logger->log(u"Binding type is not supported (yet)"_qs, Log_Compiler,
+ binding.sourceLocation());
break;
}
}
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index 0574b03088..589c70bafe 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -91,13 +91,13 @@ private:
void compileBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor);
- bool hasErrors() const { return m_logger->hasErrors() || m_logger->hasWarnings(); }
+ bool hasErrors() const { return m_logger->hasErrors(); }
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
QQmlJSLoggerCategory category = Log_Compiler)
{
// pretty much any compiler error is a critical error (we cannot
// generate code - compilation fails)
- m_logger->logCritical(message, category, location);
+ m_logger->log(message, category, location);
}
void recordError(const QV4::CompiledData::Location &location, const QString &message,
QQmlJSLoggerCategory category = Log_Compiler)
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index e0c587b5f3..d78be5a652 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -147,7 +147,7 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
current.init.body << u"// 4. call finalize in the document root"_qs;
current.init.body << u"if (canFinalize) {"_qs;
current.init.body << QStringLiteral(" %1(creator, engine, /* finalize */ true);")
- .arg(current.finalize.name);
+ .arg(current.endInit.name);
current.init.body << u"}"_qs;
}
current.init.body << u"return context;"_qs;
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 9d000cbc11..0a3e7617bd 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -92,6 +92,12 @@ struct QmltcMethodBase
QStringList body; // C++ function code
QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
QStringList declarationPrefixes;
+ QStringList modifiers; // cv-qualifiers, ref-qualifier, noexcept, attributes
+
+ // TODO: these are only needed for Component.onCompleted/onDestruction. this
+ // has to be re-written anyhow later
+ QStringList firstLines; // C++ to run at the very beginning of a function
+ QStringList lastLines; // C++ to run at the very end of a function
};
// Represents QML -> C++ compiled function
@@ -99,6 +105,9 @@ struct QmltcMethod : QmltcMethodBase
{
QString returnType; // C++ return type
QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::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
@@ -107,6 +116,11 @@ struct QmltcCtor : QmltcMethodBase
QStringList initializerList; // C++ ctor's initializer list
};
+// Represents C++ dtor of a type
+struct QmltcDtor : QmltcMethodBase
+{
+};
+
// Represents QML -> C++ compiled type
struct QmltcType
{
@@ -120,10 +134,15 @@ struct QmltcType
QList<QmltcType> children; // these are pretty much always empty
// special member functions:
- QmltcCtor basicCtor = {}; // does basic contruction
- QmltcCtor fullCtor = {}; // calls basicCtor, calls init
- QmltcMethod init = {}; // starts object initialization (context setup), calls finalize
- QmltcMethod finalize = {}; // finalizes object (bindings, special interface calls, etc.)
+ QmltcCtor baselineCtor {}; // does basic contruction
+ QmltcCtor externalCtor {}; // calls basicCtor, calls init
+ QmltcMethod init {}; // starts object initialization (context setup), calls finalize
+ QmltcMethod endInit {}; // ends object initialization (with binding setup)
+ QmltcMethod completeComponent {}; // calls componentComplete()
+ QmltcMethod finalizeComponent {}; // calls componentFinalized()
+ QmltcMethod handleOnCompleted {}; // calls Component.onCompleted
+
+ std::optional<QmltcDtor> dtor {};
// member functions: methods, signals and slots
QList<QmltcMethod> functions;
@@ -133,6 +152,9 @@ struct QmltcType
// QML document root specific:
std::optional<QmltcVariable> 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
};
// Represents whole QML program, compiled to C++
diff --git a/tools/qmltc/prototype/typeresolver.cpp b/tools/qmltc/qmltctyperesolver.cpp
index 377787830e..2d46cf38b3 100644
--- a/tools/qmltc/prototype/typeresolver.cpp
+++ b/tools/qmltc/qmltctyperesolver.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
@@ -26,8 +26,7 @@
**
****************************************************************************/
-#include "prototype/typeresolver.h"
-#include "prototype/visitor.h"
+#include "qmltctyperesolver.h"
#include <private/qqmljsimporter_p.h>
#include <private/qv4value_p.h>
@@ -37,17 +36,9 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qdiriterator.h>
-Q_LOGGING_CATEGORY(lcTypeResolver2, "qml.compiler.typeresolver", QtInfoMsg);
+Q_LOGGING_CATEGORY(lcTypeResolver2, "qml.qmltc.typeresolver", QtInfoMsg);
-namespace Qmltc {
-
-TypeResolver::TypeResolver(QQmlJSImporter *importer)
- : QQmlJSTypeResolver(importer), m_importer(importer)
-{
- Q_ASSERT(m_importer);
-}
-
-void TypeResolver::init(Visitor &visitor, QQmlJS::AST::Node *program)
+void QmltcTypeResolver::init(QmltcVisitor &visitor, QQmlJS::AST::Node *program)
{
QQmlJSTypeResolver::init(&visitor, program);
m_root = visitor.result();
@@ -67,14 +58,16 @@ void TypeResolver::init(Visitor &visitor, QQmlJS::AST::Node *program)
}
}
-QQmlJSScope::Ptr TypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
+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> TypeResolver::importedType(const QQmlJSScope::ConstPtr &type) const
+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) {
@@ -84,4 +77,3 @@ QPair<QString, QQmlJSScope::Ptr> TypeResolver::importedType(const QQmlJSScope::C
return {};
return { it.key(), it.value() };
}
-}
diff --git a/tools/qmltc/qmltctyperesolver.h b/tools/qmltc/qmltctyperesolver.h
index 495f78b81d..ae9e98c23e 100644
--- a/tools/qmltc/qmltctyperesolver.h
+++ b/tools/qmltc/qmltctyperesolver.h
@@ -29,6 +29,8 @@
#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>
@@ -39,7 +41,27 @@ QT_BEGIN_NAMESPACE
class QmltcTypeResolver : public QQmlJSTypeResolver
{
public:
- QmltcTypeResolver(QQmlJSImporter *importer) : QQmlJSTypeResolver(importer) {}
+ QmltcTypeResolver(QQmlJSImporter *importer) : QQmlJSTypeResolver(importer), m_importer(importer)
+ {
+ Q_ASSERT(importer);
+ }
+
+ void init(QmltcVisitor &visitor, QQmlJS::AST::Node *program);
+
+ // TODO: this shouldn't be exposed. instead, all the custom passes on
+ // QQmlJSScope types must happen inside Visitor
+ QQmlJSScope::Ptr root() const { return m_root; }
+
+ 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
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 691496d7c7..b70e3fe329 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -71,8 +71,8 @@ void QmltcVisitor::findCppIncludes()
if (t != type && visitType(t))
return;
- QString includeFile = t->fileName();
- if (!includeFile.isEmpty())
+ QString includeFile = t->filePath();
+ if (includeFile.endsWith(u".h"))
m_cppIncludes.insert(std::move(includeFile));
};
@@ -97,7 +97,7 @@ void QmltcVisitor::findCppIncludes()
if (visitType(t))
break;
// look in type
- if (auto includeFile = t->fileName(); !includeFile.isEmpty())
+ if (auto includeFile = t->filePath(); includeFile.endsWith(u".h"))
m_cppIncludes.insert(std::move(includeFile));
// look in properties
@@ -105,8 +105,8 @@ void QmltcVisitor::findCppIncludes()
for (const QQmlJSMetaProperty &p : properties) {
populateFromType(p.type());
- if (p.isPrivate()) {
- const QString ownersInclude = t->fileName();
+ 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));
@@ -187,37 +187,45 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
// augment property: set its write/read/etc. methods
if (publicMember->type == QQmlJS::AST::UiPublicMember::Property) {
const auto name = publicMember->name.toString();
- QQmlJSMetaProperty prop = m_currentScope->ownProperty(name);
- const QString nameWithUppercase = name[0].toUpper() + name.sliced(1);
- prop.setRead(name);
- if (prop.isWritable())
- prop.setWrite(u"set" + nameWithUppercase);
- prop.setBindable(u"bindable" + nameWithUppercase);
- prop.setNotify(name + u"Changed");
+
+ // TODO: we should set the composite type property methods here, but as
+ // of now this is done in the pass over the types after the ast
+ // traversal
+
+ const QString notifyName = name + u"Changed"_qs;
// also check that notify is already a method of m_currentScope
{
- const auto methods = m_currentScope->ownMethods(prop.notify());
+ const auto methods = m_currentScope->ownMethods(notifyName);
if (methods.size() != 1) {
const QString errorString =
methods.isEmpty() ? u"no signal"_qs : u"too many signals"_qs;
- m_logger->logCritical(
+ m_logger->log(
u"internal error: %1 found for property '%2'"_qs.arg(errorString, name),
Log_Compiler, publicMember->identifierToken);
return false;
} else if (methods[0].methodType() != QQmlJSMetaMethod::Signal) {
- m_logger->logCritical(
- u"internal error: method %1 of property %2 must be a signal"_qs.arg(
- prop.notify(), name),
- Log_Compiler, publicMember->identifierToken);
+ m_logger->log(u"internal error: method %1 of property %2 must be a signal"_qs.arg(
+ notifyName, name),
+ Log_Compiler, publicMember->identifierToken);
return false;
}
}
- m_currentScope->addOwnProperty(prop);
}
return true;
}
+bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component)
+{
+ if (!QQmlJSImportVisitor::visit(component))
+ return false;
+ m_logger->log(u"Inline components are not supported"_qs, Log_Compiler,
+ component->firstSourceLocation());
+ // despite the failure, return true here so that we do not assert in
+ // QQmlJSImportVisitor::endVisit(UiInlineComponent)
+ return true;
+}
+
void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
{
QQmlJSImportVisitor::endVisit(program);
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
index d69c84d3ca..a543a4271c 100644
--- a/tools/qmltc/qmltcvisitor.h
+++ b/tools/qmltc/qmltcvisitor.h
@@ -56,10 +56,10 @@ public:
bool visit(QQmlJS::AST::UiPublicMember *) override;
+ bool visit(QQmlJS::AST::UiInlineComponent *) override;
+
void endVisit(QQmlJS::AST::UiProgram *) override;
- // NB: overwrite result() method to return ConstPtr
- QQmlJSScope::ConstPtr result() const { return QQmlJSImportVisitor::result(); }
QList<QQmlJSScope::ConstPtr> qmlScopesWithQmlBases() const { return m_qmlTypesWithQmlBases; }
QSet<QString> cppIncludeFiles() const { return m_cppIncludes; }