aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dependencies.yaml10
-rw-r--r--examples/platforms/android/.gitignore14
-rw-r--r--examples/platforms/android/CMakeLists.txt1
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt18
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle64
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml26
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt31
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt138
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml74
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml30
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml164
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webpbin0 -> 4118 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webpbin0 -> 8980 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webpbin0 -> 6056 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webpbin0 -> 2650 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webpbin0 -> 5556 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webpbin0 -> 3680 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webpbin0 -> 5924 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webpbin0 -> 13218 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webpbin0 -> 8836 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webpbin0 -> 10144 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webpbin0 -> 25688 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webpbin0 -> 14828 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webpbin0 -> 14820 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webpbin0 -> 41052 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webpbin0 -> 21224 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml10
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml12
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml16
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml13
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml19
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle5
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties23
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle17
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc5
-rw-r--r--src/effects/qquickmultieffect.cpp5
-rw-r--r--src/effects/qquickmultieffect_p_p.h2
-rw-r--r--src/imports/tooling/Component.qml11
-rw-r--r--src/imports/tooling/Module.qml2
-rw-r--r--src/labs/models/qqmldelegatecomponent.cpp8
-rw-r--r--src/labs/models/qqmltablemodelcolumn.cpp82
-rw-r--r--src/labs/platform/CMakeLists.txt106
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.cpp6
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp4
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/Qt6QmlMacros.cmake6
-rw-r--r--src/qml/common/qjsnumbercoercion.cpp18
-rw-r--r--src/qml/common/qjsnumbercoercion.h17
-rw-r--r--src/qml/common/qv4compileddata_p.h2
-rw-r--r--src/qml/compiler/qv4codegen.cpp11
-rw-r--r--src/qml/compiler/qv4codegen_p.h3
-rw-r--r--src/qml/debugger/qqmldebugservice.cpp8
-rw-r--r--src/qml/jsapi/qjsengine.cpp1
-rw-r--r--src/qml/jsapi/qjslist.h80
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp1
-rw-r--r--src/qml/jsapi/qjsvalue.cpp1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp24
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp2
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h4
-rw-r--r--src/qml/jsruntime/qv4function.cpp74
-rw-r--r--src/qml/jsruntime/qv4function_p.h27
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h42
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp14
-rw-r--r--src/qml/jsruntime/qv4jsonobject_p.h7
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp50
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h21
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper.cpp126
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper_p.h66
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp130
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h37
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp7
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp16
-rw-r--r--src/qml/memory/qv4mm.cpp12
-rw-r--r--src/qml/qml/qqml.cpp16
-rw-r--r--src/qml/qml/qqmlbinding.cpp2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp10
-rw-r--r--src/qml/qml/qqmlengine_p.h4
-rw-r--r--src/qml/qml/qqmlimport.cpp4
-rw-r--r--src/qml/qml/qqmlmetatype.cpp8
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h4
-rw-r--r--src/qml/qml/qqmlprivate.h13
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp57
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h58
-rw-r--r--src/qml/qml/qqmltype_p.h13
-rw-r--r--src/qml/qml/qqmltype_p_p.h24
-rw-r--r--src/qml/qqmlbuiltins_p.h112
-rw-r--r--src/qmlcompiler/CMakeLists.txt3
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp543
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h56
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp314
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h28
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h40
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp80
-rw-r--r--src/qmlcompiler/qqmljscompiler_p.h4
-rw-r--r--src/qmlcompiler/qqmljscontextualtypes_p.h29
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp13
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp18
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp2
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp2
-rw-r--r--src/qmlcompiler/qqmljslintercodegen.cpp14
-rw-r--r--src/qmlcompiler/qqmljsoptimizations.cpp531
-rw-r--r--src/qmlcompiler/qqmljsoptimizations_p.h65
-rw-r--r--src/qmlcompiler/qqmljsregistercontent.cpp31
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp18
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h9
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp16
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck_p.h9
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp19
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer_p.h8
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp10
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader_p.h1
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp367
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h18
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp43
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h11
-rw-r--r--src/qmlcompiler/qqmljsutils_p.h8
-rw-r--r--src/qmldom/qqmldomexternalitems.cpp9
-rw-r--r--src/qmldom/qqmldomexternalitems_p.h1
-rw-r--r--src/qmldom/qqmldommoduleindex.cpp8
-rw-r--r--src/qmldom/qqmldomtop.cpp2
-rw-r--r--src/qmlls/qqmllintsuggestions.cpp2
-rw-r--r--src/qmlls/qqmlrenamesymbolsupport.cpp5
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp55
-rw-r--r--src/qmlmodels/qqmldmabstractitemmodeldata.cpp4
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel.cpp87
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel_p_p.h2
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings.cpp2
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp312
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor_p.h29
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrarconstants_p.h2
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription.cpp48
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription_p.h11
-rw-r--r--src/qmltyperegistrar/qqmltypescreator.cpp145
-rw-r--r--src/qmltyperegistrar/qqmltypescreator_p.h1
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp8
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp22
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h1
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp2
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickitem.cpp32
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/items/qquickitemanimation.cpp5
-rw-r--r--src/quick/items/qquicklistview.cpp2
-rw-r--r--src/quick/items/qquickmousearea.cpp1
-rw-r--r--src/quick/items/qquicktext.cpp4
-rw-r--r--src/quick/items/qquicktextdocument.cpp6
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp14
-rw-r--r--src/quick/items/qquickwindow.cpp29
-rw-r--r--src/quick/items/qquickwindow_p.h1
-rw-r--r--src/quick/items/qquickwindowmodule.cpp31
-rw-r--r--src/quick/items/qquickwindowmodule_p_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp4
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp2
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp2
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp2
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp32
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h2
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp8
-rw-r--r--src/quick/util/qquickspringanimation.cpp6
-rw-r--r--src/quick/util/qquickstate.cpp40
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp4
-rw-r--r--src/quickcontrols/material/Popup.qml2
-rw-r--r--src/quickcontrols/qquickattachedpropertypropagator.cpp3
-rw-r--r--src/quickcontrols/qquickstyle.cpp1
-rw-r--r--src/quicklayouts/qquicklinearlayout.cpp2
-rw-r--r--src/quicktemplates/qquickpopup.cpp22
-rw-r--r--src/quickvectorimage/generator/qquickgenerator.cpp2
-rw-r--r--src/quickvectorimage/generator/qquickgenerator_p.h2
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator.cpp62
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator_p.h2
-rw-r--r--src/quickvectorimage/generator/qquicknodeinfo_p.h17
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator.cpp23
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator_p.h2
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl.cpp167
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl_p.h4
-rw-r--r--src/quickvectorimage/generator/utils_p.h66
-rw-r--r--src/quickvectorimage/qquickvectorimage.cpp2
-rw-r--r--src/quickvectorimage/qquickvectorimage_p.h2
-rw-r--r--src/quickvectorimage/qquickvectorimage_p_p.h2
-rw-r--r--src/quickvectorimage/qquickvectorimageglobal_p.h2
-rw-r--r--tests/auto/cmake/CMakeLists.txt3
-rw-r--r--tests/auto/cmake/qmltc_build_failures/CMakeLists.txt7
-rw-r--r--tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/CMakeLists.txt26
-rw-r--r--tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/main.cpp3
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt1
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt9
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt7
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/main.cpp33
-rw-r--r--tests/auto/core/CMakeLists.txt4
-rw-r--r--tests/auto/qml/CMakeLists.txt8
-rwxr-xr-xtests/auto/qml/ecmascripttests/test262.py611
-rw-r--r--tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.h1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp21
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp18
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml9
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml5
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml6
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml7
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml5
-rw-r--r--tests/auto/qml/qmllint/lintplugin.cpp2
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp44
-rw-r--r--tests/auto/qml/qmltc_qprocess/CMakeLists.txt5
-rw-r--r--tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h27
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml18
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml18
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml (renamed from tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/Main.qml)0
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml10
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp45
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h (renamed from tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h)1
-rw-r--r--tests/auto/qml/qmltyperegistrar/missingTypes.json59
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp45
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h2
-rw-r--r--tests/auto/qml/qqmlengine/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml18
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp21
-rw-r--r--tests/auto/qml/qqmlengine/variantlistQJsonConversion.h53
-rw-r--r--tests/auto/qml/qqmljsscope/data/ownModuleName.qml10
-rw-r--r--tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp31
-rw-r--r--tests/auto/qml/qqmllanguage/data/typedObjectList.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp17
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp6
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp12
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp4
-rw-r--r--tests/auto/qml/qv4mm/data/simpleObject.qml3
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp115
-rw-r--r--tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml6
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.h7
-rw-r--r--tests/auto/qmlls/cli/tst_qmlls_cli.cpp2
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.cpp120
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.h2
-rw-r--r--tests/auto/qmlls/qmlls/tst_qmlls.cpp8
-rw-r--r--tests/auto/quick/qquickitem2/CMakeLists.txt5
-rw-r--r--tests/auto/quick/qquickitem2/data/embedded.qml30
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp73
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml57
-rw-r--r--tests/auto/quick/qquickpixmapcache/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml5
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp36
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_dial.qml38
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_popup.qml122
-rw-r--r--tests/auto/quickcontrols/focus/tst_focus.cpp6
-rw-r--r--tests/auto/quickcontrols/platform/tst_platform.cpp9
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp4
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp8
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp28
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp14
-rw-r--r--tests/baseline/CMakeLists.txt6
-rw-r--r--tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp4
-rw-r--r--tests/manual/CMakeLists.txt1
-rw-r--r--tests/manual/e2e/CMakeLists.txt4
-rw-r--r--tests/manual/e2e/qml/CMakeLists.txt6
-rw-r--r--tests/manual/e2e/qml/qmlformat/CMakeLists.txt27
-rw-r--r--tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp309
-rw-r--r--tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg11
-rw-r--r--tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg12
-rw-r--r--tests/manual/svg/data/styling/stroking_dash.svg10
-rw-r--r--tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg12
-rw-r--r--tests/manual/svg/svgmanager.cpp2
-rw-r--r--tests/manual/windowembedding/CMakeLists.txt21
-rw-r--r--tools/qmljsrootgen/main.cpp8
-rw-r--r--tools/qmltc/main.cpp2
279 files changed, 5242 insertions, 2954 deletions
diff --git a/dependencies.yaml b/dependencies.yaml
index f88a0d1bb1..f4b9887c09 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../qtbase:
- ref: 488545ca72b7f2a59401a42c2c264f38916e15d1
+ ref: 71bbc35a3774ba7411970ff74068f5211b73e425
required: true
../qtimageformats:
- ref: d2df8c0e1dfa3449583eb9b5b8b457fd0f17e804
+ ref: 35d57cad04bb7e75c5eb4b7ac69bef7f7e8e9d1c
required: false
../qtlanguageserver:
- ref: 1b46b73797083e84d9104a4881832d0b0eb3e810
+ ref: 631a7b727689bf55fbef6fd4ea4b0a1126432dce
required: false
../qtshadertools:
- ref: a09d290d0e1f81918ee6bce9736d30d42cf06b4e
+ ref: ea1d918c13a9681737e7506e9ae4951228100ba8
required: false
../qtsvg:
- ref: 158e9e6a0380619c4b8c57a0161883c01a626325
+ ref: 3529d5798ecd053f0c2f2a5d16b0238fe6c76ca7
required: false
diff --git a/examples/platforms/android/.gitignore b/examples/platforms/android/.gitignore
index 59cf3f824f..e1bb0d95b4 100644
--- a/examples/platforms/android/.gitignore
+++ b/examples/platforms/android/.gitignore
@@ -1,10 +1,10 @@
*.user
build/
-qml_in_java_based_android_project/.idea/
-qml_in_java_based_android_project/.gradle/
-qml_in_java_based_android_project/local.properties
-qml_in_java_based_android_project/app/libs/
-qml_in_java_based_android_project/app/src/main/res/xml/qtprovider_paths.xml
-qml_in_java_based_android_project/app/src/main/res/values/libs.xml
-qml_in_java_based_android_project/app/assets/android_rcc_bundle.rcc
+**/.idea/
+**/.gradle/
+**/local.properties
+**/app/libs/
+**/app/src/main/res/xml/qtprovider_paths.xml
+**/app/src/main/res/values/libs.xml
+**/app/assets/android_rcc_bundle.rcc
diff --git a/examples/platforms/android/CMakeLists.txt b/examples/platforms/android/CMakeLists.txt
index 2cff669bdc..7ed54f821d 100644
--- a/examples/platforms/android/CMakeLists.txt
+++ b/examples/platforms/android/CMakeLists.txt
@@ -3,3 +3,4 @@
qt_internal_add_example(qml_in_android_view)
qt_internal_add_example(qml_in_java_based_android_project)
+qt_internal_add_example(qml_in_kotlin_based_android_project)
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt b/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt
new file mode 100644
index 0000000000..efa2575b8f
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+cmake_minimum_required(VERSION 3.16)
+
+project(qml_in_kotlin_based_android_project VERSION 0.1 LANGUAGES CXX)
+
+install(DIRECTORY
+ gradle
+ app
+ DESTINATION .
+)
+install(FILES
+ settings.gradle
+ gradle.properties
+ build.gradle
+ CMakeLists.txt
+ DESTINATION .
+)
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle
new file mode 100644
index 0000000000..be631746b6
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle
@@ -0,0 +1,64 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.qml_in_kotlin_based_android_project'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.example.qml_in_kotlin_based_android_project"
+ minSdk 26
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildFeatures {
+ viewBinding = true
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ packagingOptions {
+ jniLibs {
+ useLegacyPackaging true
+ }
+ }
+ sourceSets {
+ main {
+ assets {
+ srcDirs 'assets'
+ }
+ jniLibs {
+ srcDirs 'libs'
+ }
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.10.1'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.11.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..fb183e2d79
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.Qml_in_kotlin_based_android_project"
+ tools:targetApi="34">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:configChanges="screenSize|screenLayout|orientation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt
new file mode 100644
index 0000000000..7770497796
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+package com.example.qml_in_kotlin_based_android_project
+
+import java.util.Collections
+import java.util.Stack
+
+internal class Colors {
+ private val recycle: Stack<Int> = Stack()
+ private val colors: Stack<Int> = Stack()
+
+ init {
+ recycle.addAll(
+ mutableListOf(
+ -0xe34997, -0xffbeb6, -0xd8ec75,
+ -0x4a3ef2, -0xc8c0da, -0x506c21,
+ -0x7e8afb
+ )
+ )
+ }
+
+ fun getColor(): String {
+ if (colors.size == 0) {
+ while (!recycle.isEmpty()) colors.push(recycle.pop())
+ Collections.shuffle(colors)
+ }
+ val color = colors.pop()
+ recycle.push(color)
+ return String.format("#%06X", 0xFFFFFF and color)
+ }
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt
new file mode 100644
index 0000000000..339eea40fe
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt
@@ -0,0 +1,138 @@
+package com.example.qml_in_kotlin_based_android_project
+
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.appcompat.app.AppCompatActivity
+import com.example.qml_in_kotlin_based_android_project.databinding.ActivityMainBinding
+import org.qtproject.qt.android.QtQuickView
+
+class MainActivity : AppCompatActivity(), QtQuickView.StatusChangeListener {
+
+ private val TAG = "myTag"
+ private val m_colors: Colors = Colors()
+ private lateinit var m_binding: ActivityMainBinding
+ private var m_qmlButtonSignalListenerId = 0
+ private var m_qmlView: QtQuickView? = null
+ private val m_statusNames = hashMapOf(
+ QtQuickView.STATUS_READY to "READY",
+ QtQuickView.STATUS_LOADING to "LOADING",
+ QtQuickView.STATUS_ERROR to "ERROR",
+ QtQuickView.STATUS_NULL to "NULL"
+ )
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ m_binding = ActivityMainBinding.inflate(layoutInflater)
+ val view = m_binding.root
+ setContentView(view)
+
+ m_binding.signalSwitch.setOnClickListener { switchListener() }
+
+ m_qmlView = QtQuickView(
+ this, "qrc:/qt/qml/qml_in_android_view/main.qml",
+ "qml_in_android_view"
+ )
+ // Set status change listener for m_qmlView
+ // listener implemented below in OnStatusChanged
+ m_qmlView!!.setStatusChangeListener(this)
+ val params: ViewGroup.LayoutParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ m_binding.qmlFrame.addView(m_qmlView, params)
+
+ m_binding.changeColorButton.setOnClickListener { onClickListener() }
+
+ // Check target device orientation on launch
+ handleOrientationChanges()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ handleOrientationChanges()
+ }
+
+ private fun handleOrientationChanges() {
+ // When specific target device display configurations (listed in AndroidManifest.xml
+ // android:configChanges) change, get display metrics and make needed changes to UI
+ val displayMetrics = DisplayMetrics()
+ windowManager.defaultDisplay.getMetrics(displayMetrics)
+ val qmlFrameLayoutParams = m_binding.qmlFrame.layoutParams
+ val linearLayoutParams = m_binding.kotlinLinear.layoutParams
+
+ if (displayMetrics.heightPixels > displayMetrics.widthPixels) {
+ m_binding.mainLinear.orientation = LinearLayout.VERTICAL
+ qmlFrameLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ qmlFrameLayoutParams.height = 0
+ linearLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ linearLayoutParams.height = 0
+ } else {
+ m_binding.mainLinear.orientation = LinearLayout.HORIZONTAL
+ qmlFrameLayoutParams.width = 0
+ qmlFrameLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ linearLayoutParams.width = 0
+ linearLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ m_binding.qmlFrame.layoutParams = qmlFrameLayoutParams
+ m_binding.kotlinLinear.layoutParams = linearLayoutParams
+ }
+
+ private fun onClickListener() {
+ // Set the QML view root object property "colorStringFormat" value to
+ // color from Colors.getColor()
+ m_qmlView!!.setProperty("colorStringFormat", m_colors.getColor())
+
+ val qmlBackgroundColor = m_qmlView!!.getProperty<String>("colorStringFormat")
+
+ // Display the QML View background color code
+ m_binding.getPropertyValueText.text = qmlBackgroundColor
+
+ // Display the QML View background color in a view
+ m_binding.colorBox.setBackgroundColor(Color.parseColor(qmlBackgroundColor))
+ }
+
+ private fun switchListener() {
+ // Disconnect QML button signal listener if switch is On using the saved signal listener Id
+ // and connect it again if switch is turned off
+ if (m_binding.signalSwitch.isChecked) {
+ Log.v(TAG, "QML button onClicked signal listener disconnected")
+ m_binding.switchText.setText(R.string.connect_qml_button_signal_listener)
+ m_qmlView!!.disconnectSignalListener(m_qmlButtonSignalListenerId)
+ } else {
+ Log.v(TAG, "QML button onClicked signal listener connected")
+ m_binding.switchText.setText(R.string.disconnect_qml_button_signal_listener)
+ m_qmlButtonSignalListenerId = m_qmlView!!.connectSignalListener<Any>(
+ "onClicked",
+ Any::class.java
+ ) { t: String?, value: Any? ->
+ Log.i(TAG, "QML button clicked")
+ m_binding.kotlinLinear.setBackgroundColor(Color.parseColor(m_colors.getColor()))
+ }
+ }
+ }
+
+ override fun onStatusChanged(status: Int) {
+ Log.v(TAG, "Status of QtQuickView: $status")
+
+ val qmlStatus = (resources.getString(R.string.qml_view_status)
+ + m_statusNames[status])
+
+ // Show current QML View status in a textview
+ m_binding.qmlStatus.text = qmlStatus
+
+ // Connect signal listener to "onClicked" signal from main.qml
+ // addSignalListener returns int which can be used later to identify the listener
+ if (status == QtQuickView.STATUS_READY && !m_binding.signalSwitch.isChecked) {
+ m_qmlButtonSignalListenerId = m_qmlView!!.connectSignalListener(
+ "onClicked", Any::class.java
+ ) { _: String?, _: Any? ->
+ Log.v(TAG, "QML button clicked")
+ m_binding.kotlinLinear.setBackgroundColor(Color.parseColor(m_colors.getColor()))
+ }
+ }
+ }
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..ca3826a46c
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..7706ab9e6d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000000..03f01fdd25
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/mainLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity"
+ android:orientation="vertical"
+ android:baselineAligned="false">
+
+ <FrameLayout
+ android:id="@+id/qmlFrame"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/kotlinLinear"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/lilac"
+ android:orientation="vertical">
+
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:gravity="center_horizontal"
+ android:includeFontPadding="false"
+ android:text="@string/kotlin"
+ android:textColor="@color/white"
+ android:textSize="24sp"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/qmlStatus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:gravity="center_horizontal"
+ android:text="@string/qml_view_status"
+ android:textColor="@color/white"/>
+
+ <LinearLayout
+ android:id="@+id/buttonAndSwitchLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:layout_marginTop="16dp">
+
+ <LinearLayout
+ android:id="@+id/buttonLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/changeColorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/change_qml_background"
+ android:textColor="@color/white" />
+
+ <Button
+ android:id="@+id/changeColorButton"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:text="@string/button"
+ android:textSize="14sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/switchLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/switchText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/disconnect_qml_button_signal_listener"
+ android:textColor="@color/white" />
+
+ <androidx.appcompat.widget.SwitchCompat
+ android:id="@+id/signalSwitch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:switchTextAppearance="@android:style/TextAppearance.Small"
+ android:textOff="@string/off"
+ android:textOn="@string/on"
+ app:switchTextAppearance="@style/switchStyle"
+ app:showText="true"
+ tools:ignore="UseSwitchCompatOrMaterialXml" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qmlColorLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/qmlViewBackgroundText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="2"
+ android:text="@string/qml_view_background_color"
+ android:textColor="@color/white" />
+
+ <TextView
+ android:id="@+id/getPropertyValueText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textColor="@color/white" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/colorBox"
+ android:layout_width="100dp"
+ android:layout_height="50dp"
+ android:layout_gravity="center_horizontal"
+ android:background="@android:color/transparent"
+ android:layout_weight="0"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000000..f043b66984
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..53e483046c
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..5646cd4957
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000000..8121c6f7c2
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..da81ab2c05
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..d68eb57118
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..818fa2b2dc
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..57d6591183
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..cf05b107f0
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..30855a994a
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..385517a7eb
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..10ec83e5f4
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..5a9dab1d44
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..62d203c390
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..52d2c11162
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..d38e4ce6b3
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+ <color name="lilac">#AF93DF</color>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..12661a6334
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+<resources>
+ <string name="app_name">qml_in_kotlin_based_android_project</string>
+ <string name="button">Change color</string>
+ <string name="kotlin">Kotlin</string>
+ <string name="change_qml_background">Tap button to change QML view background color</string>
+ <string name="disconnect_qml_button_signal_listener">Tap switch to disconnect QML button signal listener</string>
+ <string name="connect_qml_button_signal_listener">Tap switch to connect QML button signal listener</string>
+ <string name="on">On</string>
+ <string name="off">Off</string>
+ <string name="qml_view_status">QML view status: </string>
+ <string name="qml_view_background_color">QML view background color:</string>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..bce864063a
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="switchStyle">
+ <item name="android:textSize">12sp</item>
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..b501d7b323
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Qml_in_kotlin_based_android_project" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000000..148c18b659
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..0c4f95cab9
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle
new file mode 100644
index 0000000000..97e3f23ff2
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+id 'com.android.application' version '8.2.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties
new file mode 100644
index 0000000000..2cbd6d19d3
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..a3353773cb
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Mar 21 11:14:46 EET 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle
new file mode 100644
index 0000000000..a30bfe06d0
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "qml_in_kotlin_based_android_project"
+include ':app'
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
index 78babe6ca5..3b4912829e 100644
--- a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
+++ b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
@@ -64,9 +64,8 @@
After copying the values we need, sync() also performs some graphics
resource initialization. The MTLDevice is queried from the scenegraph. Once
a MTLTexture is available, a QSGTexture wrapping (not owning) it is created
- via QQuickWindow::createTextureFromNativeObject(). This function is a
- modern equivalent to QQuickWindow::createTextureFromId() that is not tied
- to OpenGL. Finally, the QSGTexture is associated with the underlying
+ via \l QNativeInterface::QSGOpenGLTexture::fromNative().
+ Finally, the QSGTexture is associated with the underlying
materials by calling the base class' setTexture() function.
\snippet scenegraph/metaltextureimport/metaltextureimport.mm 6
diff --git a/src/effects/qquickmultieffect.cpp b/src/effects/qquickmultieffect.cpp
index 6b20c3fa14..7561649fbf 100644
--- a/src/effects/qquickmultieffect.cpp
+++ b/src/effects/qquickmultieffect.cpp
@@ -1697,11 +1697,12 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
}
// Set the blur items source components
- static const auto dummyShaderSource = new QQuickShaderEffectSource(q);
+ if (!m_dummyShaderSource)
+ m_dummyShaderSource = new QQuickShaderEffectSource(q);
for (int i = 0; i < m_blurEffects.size(); i++) {
auto *blurEffect = m_blurEffects[i];
auto sourceItem = (i >= itemsAmount) ?
- static_cast<QQuickItem *>(dummyShaderSource) : (i == 0) ?
+ static_cast<QQuickItem *>(m_dummyShaderSource) : (i == 0) ?
static_cast<QQuickItem *>(m_shaderSource->output()) :
static_cast<QQuickItem *>(m_blurEffects[i - 1]);
auto sourceVariant = QVariant::fromValue<QQuickItem*>(sourceItem);
diff --git a/src/effects/qquickmultieffect_p_p.h b/src/effects/qquickmultieffect_p_p.h
index 2df33db45b..7d635e2e25 100644
--- a/src/effects/qquickmultieffect_p_p.h
+++ b/src/effects/qquickmultieffect_p_p.h
@@ -27,6 +27,7 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
class QQuickShaderEffect;
+class QQuickShaderEffectSource;
class QQuickMultiEffectPrivate : public QQuickItemPrivate
{
@@ -145,6 +146,7 @@ private:
QQuickItem *m_sourceItem = nullptr;
QGfxSourceProxy *m_shaderSource = nullptr;
QQuickShaderEffect *m_shaderEffect = nullptr;
+ QQuickShaderEffectSource *m_dummyShaderSource = nullptr;
QVector<QQuickShaderEffect *> m_blurEffects;
bool m_autoPaddingEnabled = true;
QRectF m_paddingRect;
diff --git a/src/imports/tooling/Component.qml b/src/imports/tooling/Component.qml
index 30644e3ac2..6a5cde3c2a 100644
--- a/src/imports/tooling/Component.qml
+++ b/src/imports/tooling/Component.qml
@@ -8,12 +8,13 @@ QtObject {
property string file
required property string name
+ property list<string> aliases: []
property string prototype
- property var exports: []
- property var exportMetaObjectRevisions: []
- property var interfaces: []
- property var deferredNames: []
- property var immediateNames: []
+ property list<string> exports: []
+ property list<int> exportMetaObjectRevisions
+ property list<string> interfaces
+ property list<string> deferredNames
+ property list<string> immediateNames
property string attachedType
property string valueType
property string extension
diff --git a/src/imports/tooling/Module.qml b/src/imports/tooling/Module.qml
index 487e235230..4aa9312a36 100644
--- a/src/imports/tooling/Module.qml
+++ b/src/imports/tooling/Module.qml
@@ -5,5 +5,5 @@ import QML
QtObject {
default property list<Component> components
- property var dependencies: []
+ property list<string> dependencies: []
}
diff --git a/src/labs/models/qqmldelegatecomponent.cpp b/src/labs/models/qqmldelegatecomponent.cpp
index 90113f24f3..01dbe7e81f 100644
--- a/src/labs/models/qqmldelegatecomponent.cpp
+++ b/src/labs/models/qqmldelegatecomponent.cpp
@@ -237,7 +237,7 @@ void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *p
QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
q->m_choices.append(choice);
connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
qsizetype QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
@@ -258,7 +258,7 @@ void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *pr
for (QQmlDelegateChoice *choice : q->m_choices)
disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
q->m_choices.clear();
- q->delegateChanged();
+ emit q->delegateChanged();
}
void QQmlDelegateChooser::choices_replace(QQmlListProperty<QQmlDelegateChoice> *prop,
@@ -270,7 +270,7 @@ void QQmlDelegateChooser::choices_replace(QQmlListProperty<QQmlDelegateChoice> *
q->m_choices[index] = choice;
connect(choice, &QQmlDelegateChoice::changed, q,
&QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
void QQmlDelegateChooser::choices_removeLast(QQmlListProperty<QQmlDelegateChoice> *prop)
@@ -278,7 +278,7 @@ void QQmlDelegateChooser::choices_removeLast(QQmlListProperty<QQmlDelegateChoice
QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
disconnect(q->m_choices.takeLast(), &QQmlDelegateChoice::changed,
q, &QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
diff --git a/src/labs/models/qqmltablemodelcolumn.cpp b/src/labs/models/qqmltablemodelcolumn.cpp
index f2e6d65f87..356cad476f 100644
--- a/src/labs/models/qqmltablemodelcolumn.cpp
+++ b/src/labs/models/qqmltablemodelcolumn.cpp
@@ -18,27 +18,50 @@ QT_BEGIN_NAMESPACE
TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles},
with the exception of \c Qt::InitialSortOrderRole.
+ Roles can be accessed by as listed below, e.g.
+ \code
+ text: display
+
+ required property string display
+ \endcode
+
+ \table
+ \row \li Qt::DisplayRole \li display
+ \row \li Qt::DecorationRole \li decoration
+ \row \li Qt::EditRole \li edit
+ \row \li Qt::ToolTipRole \li toolTip
+ \row \li Qt::StatusTipRole \li statusTip
+ \row \li Qt::WhatsThisRole \li whatsThis
+ \row \li Qt::FontRole \li font
+ \row \li Qt::TextAlignmentRole \li textAlignment
+ \row \li Qt::BackgroundRole \li background
+ \row \li Qt::ForegroundRole \li foreground
+ \row \li Qt::CheckStateRole \li checkState
+ \row \li Qt::AccessibleTextRole \li accessibleText
+ \row \li Qt::AccessibleDescriptionRole \li accessibleDescription
+ \row \li Qt::SizeHintRole \li sizeHintRoleNam
+ \endtable
\sa TableModel, TableView
*/
-static const QString displayRoleName = QStringLiteral("display");
-static const QString decorationRoleName = QStringLiteral("decoration");
-static const QString editRoleName = QStringLiteral("edit");
-static const QString toolTipRoleName = QStringLiteral("toolTip");
-static const QString statusTipRoleName = QStringLiteral("statusTip");
-static const QString whatsThisRoleName = QStringLiteral("whatsThis");
+static constexpr QLatin1StringView displayRoleName("display");
+static constexpr QLatin1StringView decorationRoleName("decoration");
+static constexpr QLatin1StringView editRoleName("edit");
+static constexpr QLatin1StringView toolTipRoleName("toolTip");
+static constexpr QLatin1StringView statusTipRoleName("statusTip");
+static constexpr QLatin1StringView whatsThisRoleName("whatsThis");
-static const QString fontRoleName = QStringLiteral("font");
-static const QString textAlignmentRoleName = QStringLiteral("textAlignment");
-static const QString backgroundRoleName = QStringLiteral("background");
-static const QString foregroundRoleName = QStringLiteral("foreground");
-static const QString checkStateRoleName = QStringLiteral("checkState");
+static constexpr QLatin1StringView fontRoleName("font");
+static constexpr QLatin1StringView textAlignmentRoleName("textAlignment");
+static constexpr QLatin1StringView backgroundRoleName("background");
+static constexpr QLatin1StringView foregroundRoleName("foreground");
+static constexpr QLatin1StringView checkStateRoleName("checkState");
-static const QString accessibleTextRoleName = QStringLiteral("accessibleText");
-static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription");
+static constexpr QLatin1StringView accessibleTextRoleName("accessibleText");
+static constexpr QLatin1StringView accessibleDescriptionRoleName("accessibleDescription");
-static const QString sizeHintRoleName = QStringLiteral("sizeHint");
+static constexpr QLatin1StringView sizeHintRoleName("sizeHint");
QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent)
@@ -143,21 +166,22 @@ const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const
const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames()
{
- QHash<int, QString> names;
- names[Qt::DisplayRole] = QLatin1String("display");
- names[Qt::DecorationRole] = QLatin1String("decoration");
- names[Qt::EditRole] = QLatin1String("edit");
- names[Qt::ToolTipRole] = QLatin1String("toolTip");
- names[Qt::StatusTipRole] = QLatin1String("statusTip");
- names[Qt::WhatsThisRole] = QLatin1String("whatsThis");
- names[Qt::FontRole] = QLatin1String("font");
- names[Qt::TextAlignmentRole] = QLatin1String("textAlignment");
- names[Qt::BackgroundRole] = QLatin1String("background");
- names[Qt::ForegroundRole] = QLatin1String("foreground");
- names[Qt::CheckStateRole] = QLatin1String("checkState");
- names[Qt::AccessibleTextRole] = QLatin1String("accessibleText");
- names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription");
- names[Qt::SizeHintRole] = QLatin1String("sizeHint");
+ static const QHash<int, QString> names {
+ {Qt::DisplayRole, displayRoleName},
+ {Qt::DecorationRole, decorationRoleName},
+ {Qt::EditRole, editRoleName},
+ {Qt::ToolTipRole, toolTipRoleName},
+ {Qt::StatusTipRole, statusTipRoleName},
+ {Qt::WhatsThisRole, whatsThisRoleName},
+ {Qt::FontRole, fontRoleName},
+ {Qt::TextAlignmentRole, textAlignmentRoleName},
+ {Qt::BackgroundRole, backgroundRoleName},
+ {Qt::ForegroundRole, foregroundRoleName},
+ {Qt::CheckStateRole, checkStateRoleName},
+ {Qt::AccessibleTextRole, accessibleTextRoleName},
+ {Qt::AccessibleDescriptionRole, accessibleDescriptionRoleName},
+ {Qt::SizeHintRole, sizeHintRoleName}
+ };
return names;
}
diff --git a/src/labs/platform/CMakeLists.txt b/src/labs/platform/CMakeLists.txt
index ff98a386ae..adb36e2216 100644
--- a/src/labs/platform/CMakeLists.txt
+++ b/src/labs/platform/CMakeLists.txt
@@ -1,17 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-#####################################################################
-## qtlabsplatformplugin Plugin:
-#####################################################################
-
-qt_internal_add_qml_module(qtlabsplatformplugin
+qt_internal_add_qml_module(LabsPlatform
URI "Qt.labs.platform"
VERSION "1.1"
CLASS_NAME QtLabsPlatformPlugin
DEPENDENCIES
QtQuick
- PLUGIN_TARGET qtlabsplatformplugin
+ QtQuick.Templates
SOURCES
qquicklabsplatformcolordialog.cpp qquicklabsplatformcolordialog_p.h
qquicklabsplatformdialog.cpp qquicklabsplatformdialog_p.h
@@ -38,54 +34,76 @@ qt_internal_add_qml_module(qtlabsplatformplugin
Qt::QuickTemplates2Private
)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets
+qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_systemtrayicon
SOURCES
- widgets/qwidgetplatform_p.h
- LIBRARIES
- Qt::Widgets
+ qquicklabsplatformsystemtrayicon.cpp qquicklabsplatformsystemtrayicon_p.h
)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_systemtrayicon AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformsystemtrayicon.cpp widgets/qwidgetplatformsystemtrayicon_p.h
-)
+if(TARGET Qt::Widgets)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND (QT_FEATURE_colordialog OR QT_FEATURE_filedialog OR QT_FEATURE_fontdialog OR QT_FEATURE_messagebox)
- SOURCES
- widgets/qwidgetplatformdialog.cpp widgets/qwidgetplatformdialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ SOURCES
+ widgets/qwidgetplatform_p.h
+ LIBRARIES
+ Qt::Widgets
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_colordialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformcolordialog.cpp widgets/qwidgetplatformcolordialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_systemtrayicon
+ SOURCES
+ widgets/qwidgetplatformsystemtrayicon.cpp widgets/qwidgetplatformsystemtrayicon_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_filedialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformfiledialog.cpp widgets/qwidgetplatformfiledialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_colordialog OR
+ QT_FEATURE_filedialog OR
+ QT_FEATURE_fontdialog OR
+ QT_FEATURE_messagebox
+ SOURCES
+ widgets/qwidgetplatformdialog.cpp widgets/qwidgetplatformdialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_fontdialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformfontdialog.cpp widgets/qwidgetplatformfontdialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_colordialog
+ SOURCES
+ widgets/qwidgetplatformcolordialog.cpp widgets/qwidgetplatformcolordialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_menu AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformmenu.cpp widgets/qwidgetplatformmenu_p.h
- widgets/qwidgetplatformmenuitem.cpp widgets/qwidgetplatformmenuitem_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_filedialog
+ SOURCES
+ widgets/qwidgetplatformfiledialog.cpp widgets/qwidgetplatformfiledialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_messagebox AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformmessagedialog.cpp widgets/qwidgetplatformmessagedialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_fontdialog
+ SOURCES
+ widgets/qwidgetplatformfontdialog.cpp widgets/qwidgetplatformfontdialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION QT_FEATURE_systemtrayicon
- SOURCES
- qquicklabsplatformsystemtrayicon.cpp qquicklabsplatformsystemtrayicon_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_menu
+ SOURCES
+ widgets/qwidgetplatformmenu.cpp widgets/qwidgetplatformmenu_p.h
+ widgets/qwidgetplatformmenuitem.cpp widgets/qwidgetplatformmenuitem_p.h
+ )
+
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_messagebox
+ SOURCES
+ widgets/qwidgetplatformmessagedialog.cpp widgets/qwidgetplatformmessagedialog_p.h
+ )
+endif()
-qt_internal_add_docs(qtlabsplatformplugin
+qt_internal_add_docs(LabsPlatform
doc/qtlabsplatform.qdocconf
)
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp
index 0a5eb1d6b9..15b947a10c 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.cpp
+++ b/src/plugins/qmllint/quick/quicklintplugin.cpp
@@ -37,7 +37,7 @@ bool ForbiddenChildrenPropertyValidatorPass::shouldRun(const QQmlSA::Element &el
if (!element.parentScope())
return false;
- for (const auto &pair : m_types.asKeyValueRange()) {
+ for (const auto &pair : std::as_const(m_types).asKeyValueRange()) {
if (element.parentScope().inherits(pair.first))
return true;
}
@@ -47,7 +47,7 @@ bool ForbiddenChildrenPropertyValidatorPass::shouldRun(const QQmlSA::Element &el
void ForbiddenChildrenPropertyValidatorPass::run(const QQmlSA::Element &element)
{
- for (const auto &elementPair : m_types.asKeyValueRange()) {
+ for (const auto &elementPair : std::as_const(m_types).asKeyValueRange()) {
const QQmlSA::Element &type = elementPair.first;
if (!element.parentScope().inherits(type))
continue;
@@ -434,7 +434,7 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass(
{
QMultiHash<QString, QQmlSA::Element> propertyTypes;
- for (const auto pair : expectedPropertyTypes.asKeyValueRange()) {
+ for (const auto &pair : expectedPropertyTypes.asKeyValueRange()) {
const QQmlSA::Element propType = pair.second.module.isEmpty()
? resolveBuiltinType(pair.second.name)
: resolveType(pair.second.module, pair.second.name);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index 3d7be2b2c1..a407153401 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -251,9 +251,9 @@ void QV4Debugger::pauseAndWait(PauseReason reason)
bool QV4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr)
{
- QHash<BreakPoint, QString>::iterator it = m_breakPoints.find(
+ const auto it = m_breakPoints.constFind(
BreakPoint(QUrl(filename).fileName(), linenr));
- if (it == m_breakPoints.end())
+ if (it == m_breakPoints.cend())
return false;
QString condition = it.value();
if (condition.isEmpty())
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
index c4b42ed1be..30d6a9db5b 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
@@ -138,8 +138,8 @@ void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset)
if (offset == path.size())
return;
- auto it = m_next.find(path.at(offset));
- if (it != m_next.end())
+ auto it = m_next.constFind(path.at(offset));
+ if (it != m_next.cend())
(*it)->remove(path, ++offset);
}
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index be753ed10f..955ec7ecae 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -62,8 +62,8 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
if (d.locationId != 0)
ds << static_cast<qint64>(d.locationId);
} else {
- auto i = locations.find(d.locationId);
- if (i != locations.end()) {
+ auto i = locations.constFind(d.locationId);
+ if (i != locations.cend()) {
ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
ds << (i->url.isEmpty() ? i->location.sourceFile : i->url.toString())
<< static_cast<qint32>(i->location.line)
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
index 1a3e17fcbc..0b50ca9f0a 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
@@ -98,10 +98,10 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
return finalizeMessages(until, messages, props.start, d);
appendMemoryEvents(props.start, messages, d);
- auto location = m_functionLocations.find(props.id);
+ auto location = m_functionLocations.constFind(props.id);
d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id);
- if (location != m_functionLocations.end()) {
+ if (location != m_functionLocations.cend()) {
messages.push_back(d.squeezedData());
d.clear();
d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
index 9107715f28..6e7c64460f 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
@@ -497,8 +497,8 @@ void QQmlDebugServerImpl::receiveMessage()
} else {
if (m_gotHello) {
- QHash<QString, QQmlDebugService *>::Iterator iter = m_plugins.find(name);
- if (iter == m_plugins.end()) {
+ const auto iter = m_plugins.constFind(name);
+ if (iter == m_plugins.cend()) {
qWarning() << "QML Debugger: Message received for missing plugin" << name << '.';
} else {
QQmlDebugService *service = *iter;
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index fa672d3d6c..cbcdc2f8f9 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -255,6 +255,7 @@ qt_internal_add_qml_module(Qml
jsruntime/qv4property_p.h
jsruntime/qv4propertykey.cpp jsruntime/qv4propertykey_p.h
jsruntime/qv4proxy.cpp jsruntime/qv4proxy_p.h
+ jsruntime/qv4qmetaobjectwrapper.cpp jsruntime/qv4qmetaobjectwrapper_p.h
jsruntime/qv4qmlcontext.cpp jsruntime/qv4qmlcontext_p.h
jsruntime/qv4qobjectwrapper.cpp jsruntime/qv4qobjectwrapper_p.h
jsruntime/qv4reflect.cpp jsruntime/qv4reflect_p.h
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 9d79bae9cd..5352f896f6 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -743,7 +743,11 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
else()
set(output_folder "${CMAKE_CURRENT_BINARY_DIR}")
endif()
- get_filename_component(build_folder "${output_folder}" DIRECTORY)
+ string(REPLACE "." ";" uri_bits "${arg_URI}")
+ set(build_folder "${output_folder}")
+ foreach(bit IN LISTS uri_bits)
+ get_filename_component(build_folder "${build_folder}" DIRECTORY)
+ endforeach()
get_directory_property(_qmlls_ini_build_folders _qmlls_ini_build_folders)
list(APPEND _qmlls_ini_build_folders "${build_folder}")
set_directory_properties(PROPERTIES _qmlls_ini_build_folders "${_qmlls_ini_build_folders}")
diff --git a/src/qml/common/qjsnumbercoercion.cpp b/src/qml/common/qjsnumbercoercion.cpp
index ba76c12bb0..8cd96a4e25 100644
--- a/src/qml/common/qjsnumbercoercion.cpp
+++ b/src/qml/common/qjsnumbercoercion.cpp
@@ -24,10 +24,26 @@ QT_BEGIN_NAMESPACE
\internal
Checks whether \a d contains a value that can serve as an index into an array.
- For that, \a d must be a non-negative value representable as an int.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
*/
/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(qint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
+*/
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(quint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a value representable as an unsigned 32bit int.
+*/
+
+/*!
\fn int QJSNumberCoercion::toInteger(double d)
\internal
diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h
index 569976454f..0023bff6e8 100644
--- a/src/qml/common/qjsnumbercoercion.h
+++ b/src/qml/common/qjsnumbercoercion.h
@@ -27,11 +27,20 @@ public:
static constexpr bool isArrayIndex(double d)
{
- if (d < 0 || !equals(d, d) || d > (std::numeric_limits<int>::max)()) {
- return false;
- }
+ return d >= 0
+ && equals(d, d)
+ && d <= (std::numeric_limits<uint>::max)()
+ && equals(static_cast<uint>(d), d);
+ }
- return equals(static_cast<int>(d), d);
+ static constexpr bool isArrayIndex(qint64 i)
+ {
+ return i >= 0 && i <= (std::numeric_limits<uint>::max)();
+ }
+
+ static constexpr bool isArrayIndex(quint64 i)
+ {
+ return i <= (std::numeric_limits<uint>::max)();
}
static constexpr int toInteger(double d) {
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 784e3e5444..79df230872 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x40 // Switch the "sticky" and "unicode" regexp flags
+#define QV4_DATA_STRUCTURE_VERSION 0x42 // Change metatype computation of AOT-compiled functions
class QIODevice;
class QQmlTypeNameCache;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 7c312d9a3e..25831eab73 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1511,7 +1511,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError())
return false;
- binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
+ binopHelper(ast, baseOp(ast->op), tempLeft, right).loadInAccumulator();
setExprResult(left.storeRetainAccumulator());
break;
@@ -1524,7 +1524,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError())
return false;
- setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
Q_FALLTHROUGH();
@@ -1559,7 +1559,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError())
return false;
- setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+ setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@@ -1568,8 +1568,11 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
}
-Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right)
+Codegen::Reference Codegen::binopHelper(BinaryExpression *ast, QSOperator::Op oper, Reference &left,
+ Reference &right)
{
+ auto loc = combine(ast->left->firstSourceLocation(), ast->right->lastSourceLocation());
+ bytecodeGenerator->setLocation(loc);
switch (oper) {
case QSOperator::Add: {
left = left.storeOnStack();
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index d9a04dcd92..3a27cb1487 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -711,7 +711,8 @@ public:
QQmlJS::DiagnosticMessage error() const;
QUrl url() const;
- Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
+ Reference binopHelper(QQmlJS::AST::BinaryExpression *ast, QSOperator::Op oper, Reference &left,
+ Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
struct Arguments { int argc; int argv; bool hasSpread; };
Arguments pushArgs(QQmlJS::AST::ArgumentList *args);
diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp
index 034aa55ce7..56cffc169c 100644
--- a/src/qml/debugger/qqmldebugservice.cpp
+++ b/src/qml/debugger/qqmldebugservice.cpp
@@ -103,8 +103,8 @@ Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash)
void ObjectReferenceHash::remove(QObject *obj)
{
- QHash<QObject *, int>::Iterator iter = objects.find(obj);
- if (iter != objects.end()) {
+ const auto iter = objects.constFind(obj);
+ if (iter != objects.cend()) {
ids.remove(iter.value());
objects.erase(iter);
}
@@ -120,9 +120,9 @@ int QQmlDebugService::idForObject(QObject *object)
return -1;
ObjectReferenceHash *hash = objectReferenceHash();
- QHash<QObject *, int>::Iterator iter = hash->objects.find(object);
+ auto iter = hash->objects.constFind(object);
- if (iter == hash->objects.end()) {
+ if (iter == hash->objects.cend()) {
int id = hash->nextId++;
hash->ids.insert(id, object);
iter = hash->objects.insert(object, id);
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 42a279ca25..8346ef5d84 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -16,6 +16,7 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
#include <private/qv4stackframe_p.h>
#include <private/qv4module_p.h>
#include <private/qv4symbol_p.h>
diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h
index f55d669a41..d604e266f2 100644
--- a/src/qml/jsapi/qjslist.h
+++ b/src/qml/jsapi/qjslist.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
struct QJSListIndexClamp
{
- static qsizetype clamp(int start, qsizetype max, qsizetype min = 0)
+ static qsizetype clamp(qsizetype start, qsizetype max, qsizetype min = 0)
{
Q_ASSERT(min >= 0);
Q_ASSERT(min <= max);
@@ -43,18 +43,15 @@ struct QJSList : private QJSListIndexClamp
QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
- Value at(int index) const
+ Value at(qsizetype index) const
{
Q_ASSERT(index >= 0 && index < size());
return *(m_list->cbegin() + index);
}
- int size() const
- {
- return int(std::min(m_list->size(), qsizetype(std::numeric_limits<int>::max())));
- }
+ qsizetype size() const { return m_list->size(); }
- void resize(int size)
+ void resize(qsizetype size)
{
m_list->resize(size);
}
@@ -64,7 +61,7 @@ struct QJSList : private QJSListIndexClamp
return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend();
}
- bool includes(const Value &value, int start) const
+ bool includes(const Value &value, qsizetype start) const
{
return std::find(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), value)
!= m_list->cend();
@@ -88,14 +85,14 @@ struct QJSList : private QJSListIndexClamp
{
return *m_list;
}
- List slice(int start) const
+ List slice(qsizetype start) const
{
List result;
std::copy(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(),
std::back_inserter(result));
return result;
}
- List slice(int start, int end) const
+ List slice(qsizetype start, qsizetype end) const
{
const qsizetype size = m_list->size();
const qsizetype clampedStart = clamp(start, size);
@@ -107,7 +104,7 @@ struct QJSList : private QJSListIndexClamp
return result;
}
- int indexOf(const Value &value) const
+ qsizetype indexOf(const Value &value) const
{
const auto begin = m_list->cbegin();
const auto end = m_list->cend();
@@ -116,9 +113,9 @@ struct QJSList : private QJSListIndexClamp
return -1;
const qsizetype result = it - begin;
Q_ASSERT(result >= 0);
- return result > std::numeric_limits<int>::max() ? -1 : int(result);
+ return result;
}
- int indexOf(const Value &value, int start) const
+ qsizetype indexOf(const Value &value, qsizetype start) const
{
const auto begin = m_list->cbegin();
const auto end = m_list->cend();
@@ -127,18 +124,17 @@ struct QJSList : private QJSListIndexClamp
return -1;
const qsizetype result = it - begin;
Q_ASSERT(result >= 0);
- return result > std::numeric_limits<int>::max() ? -1 : int(result);
+ return result;
}
- int lastIndexOf(const Value &value) const
+ qsizetype lastIndexOf(const Value &value) const
{
const auto begin = std::make_reverse_iterator(m_list->cend());
const auto end = std::make_reverse_iterator(m_list->cbegin());
const auto it = std::find(begin, end, value);
- const qsizetype result = (end - it) - 1;
- return result > std::numeric_limits<int>::max() ? -1 : int(result);
+ return (end - it) - 1;
}
- int lastIndexOf(const Value &value, int start) const
+ qsizetype lastIndexOf(const Value &value, qsizetype start) const
{
const qsizetype size = m_list->size();
if (size == 0)
@@ -150,8 +146,7 @@ struct QJSList : private QJSListIndexClamp
const auto end = std::make_reverse_iterator(m_list->cbegin());
const auto it = std::find(begin, end, value);
- const qsizetype result = (end - it) - 1;
- return result > std::numeric_limits<int>::max() ? -1 : int(result);
+ return (end - it) - 1;
}
QString toString() const { return join(); }
@@ -168,18 +163,18 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
- QObject *at(int index) const
+ QObject *at(qsizetype index) const
{
Q_ASSERT(index >= 0 && index < size());
return m_list->at(m_list, index);
}
- int size() const
+ qsizetype size() const
{
- return int(std::min(m_list->count(m_list), qsizetype(std::numeric_limits<int>::max())));
+ return m_list->count(m_list);
}
- void resize(int size)
+ void resize(qsizetype size)
{
qsizetype current = m_list->count(m_list);
if (current < size && m_list->append) {
@@ -206,7 +201,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
return false;
}
- bool includes(const QObject *value, int start) const
+ bool includes(const QObject *value, qsizetype start) const
{
if (!m_list->count || !m_list->at)
return false;
@@ -239,7 +234,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
{
return m_list->toList<QObjectList>();
}
- QObjectList slice(int start) const
+ QObjectList slice(qsizetype start) const
{
if (!m_list->count || !m_list->at)
return QObjectList();
@@ -252,7 +247,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
result.append(m_list->at(m_list, i));
return result;
}
- QObjectList slice(int start, int end) const
+ QObjectList slice(qsizetype start, qsizetype end) const
{
if (!m_list->count || !m_list->at)
return QObjectList();
@@ -267,46 +262,43 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
return result;
}
- int indexOf(const QObject *value) const
+ qsizetype indexOf(const QObject *value) const
{
if (!m_list->count || !m_list->at)
return -1;
- const qsizetype end
- = std::min(m_list->count(m_list), qsizetype(std::numeric_limits<int>::max()));
+ const qsizetype end = m_list->count(m_list);
for (qsizetype i = 0; i < end; ++i) {
if (m_list->at(m_list, i) == value)
- return int(i);
+ return i;
}
return -1;
}
- int indexOf(const QObject *value, int start) const
+ qsizetype indexOf(const QObject *value, qsizetype start) const
{
if (!m_list->count || !m_list->at)
return -1;
const qsizetype size = m_list->count(m_list);
- for (qsizetype i = clamp(start, size),
- end = std::min(size, qsizetype(std::numeric_limits<int>::max()));
- i < end; ++i) {
+ for (qsizetype i = clamp(start, size); i < size; ++i) {
if (m_list->at(m_list, i) == value)
- return int(i);
+ return i;
}
return -1;
}
- int lastIndexOf(const QObject *value) const
+ qsizetype lastIndexOf(const QObject *value) const
{
if (!m_list->count || !m_list->at)
return -1;
for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) {
if (m_list->at(m_list, i) == value)
- return i > std::numeric_limits<int>::max() ? -1 : int(i);
+ return i;
}
return -1;
}
- int lastIndexOf(const QObject *value, int start) const
+ qsizetype lastIndexOf(const QObject *value, qsizetype start) const
{
if (!m_list->count || !m_list->at)
return -1;
@@ -318,7 +310,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam
qsizetype clampedStart = std::min(clamp(start, size), size - 1);
for (qsizetype i = clampedStart; i >= 0; --i) {
if (m_list->at(m_list, i) == value)
- return i > std::numeric_limits<int>::max() ? -1 : int(i);
+ return i;
}
return -1;
}
@@ -342,11 +334,11 @@ public:
}
bool hasNext() const { return m_index < m_size; }
- int next() { return m_index++; }
+ qsizetype next() { return m_index++; }
private:
- int m_index;
- int m_size;
+ qsizetype m_index;
+ qsizetype m_size;
};
// QJSListForInIterator must not require initialization so that we can jump over it with goto.
@@ -365,7 +357,7 @@ public:
Value next(const QJSList<List, Value> &list) { return list.at(m_index++); }
private:
- int m_index;
+ qsizetype m_index;
};
// QJSListForOfIterator must not require initialization so that we can jump over it with goto.
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
index a4f2f97432..452f991a26 100644
--- a/src/qml/jsapi/qjsmanagedvalue.cpp
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -13,6 +13,7 @@
#include <QtQml/private/qv4urlobject_p.h>
#include <QtQml/private/qv4variantobject_p.h>
#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <QtQml/private/qv4qmetaobjectwrapper_p.h>
#include <QtQml/private/qv4regexpobject_p.h>
#include <QtQml/private/qv4dateobject_p.h>
#include <QtQml/private/qv4errorobject_p.h>
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 5d320809dd..f6b97262c3 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -19,6 +19,7 @@
#include <private/qv4mm_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
#include <private/qv4urlobject_p.h>
#include <private/qqmlbuiltins_p.h>
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index acf6132799..a2a2e99a01 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -61,6 +61,7 @@
#include "qv4variantobject_p.h"
#include "qv4sequenceobject_p.h"
#include "qv4qobjectwrapper_p.h"
+#include "qv4qmetaobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4dataview_p.h"
@@ -2172,15 +2173,15 @@ ExecutionEngine::Module ExecutionEngine::moduleForUrl(
ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const ExecutableCompilationUnit *referrer)
{
- const auto nativeModule = nativeModules.find(url);
- if (nativeModule != nativeModules.end())
+ const auto nativeModule = nativeModules.constFind(url);
+ if (nativeModule != nativeModules.cend())
return Module { nullptr, *nativeModule };
const QUrl resolved = referrer
? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
: QQmlTypeLoader::normalize(url);
- auto existingModule = m_compilationUnits.find(resolved);
- if (existingModule != m_compilationUnits.end())
+ auto existingModule = m_compilationUnits.constFind(resolved);
+ if (existingModule != m_compilationUnits.cend())
return Module { *existingModule, nullptr };
auto newModule = compileModule(resolved);
@@ -2191,8 +2192,8 @@ ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const Execu
QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Value &module)
{
- const auto existingModule = nativeModules.find(url);
- if (existingModule != nativeModules.end())
+ const auto existingModule = nativeModules.constFind(url);
+ if (existingModule != nativeModules.cend())
return nullptr;
QV4::Value *val = this->memoryManager->m_persistentValues->allocate();
@@ -2686,16 +2687,15 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
return true;
}
- if (metaType == QMetaType::fromType<QQmlListReference>()) {
- if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
*reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
return true;
}
- }
- if (metaType == QMetaType::fromType<QQmlListProperty<QObject>>()) {
- if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
- *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapper->d()->property();
+ const auto wrapperPrivate = wrapper->d();
+ if (wrapperPrivate->propertyType() == metaType) {
+ *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property();
return true;
}
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 9e10e437a8..34d737cdae 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -157,7 +157,7 @@ void ExecutableCompilationUnit::populate()
auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * {
if (aotFunction) {
if (aotFunction->functionPtr) {
- if (aotFunction->extraData == i)
+ if (aotFunction->functionIndex == i)
return aotFunction++;
} else {
aotFunction = nullptr;
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index a67086e57a..3f3335ef4e 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -268,8 +268,8 @@ private:
IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ auto it = namedObjectsPerComponentCache.constFind(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.cend()))
return createNamedObjectsPerComponent(componentObjectIndex);
Q_ASSERT(!it->isEmpty());
return *it;
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index caba2b1a9a..ae36b563e0 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -61,14 +61,14 @@ ReturnedValue Function::call(
switch (kind) {
case AotCompiled:
return QV4::convertAndCall(
- context->engine(), aotCompiledFunction, thisObject, argv, argc,
+ context->engine(), &aotCompiledFunction, thisObject, argv, argc,
[this, context](
QObject *thisObject, void **a, const QMetaType *types, int argc) {
call(thisObject, a, types, argc, context);
});
case JsTyped:
return QV4::coerceAndCall(
- context->engine(), jsTypedFunction, compiledFunction, argv, argc,
+ context->engine(), &jsTypedFunction, compiledFunction, argv, argc,
[this, context, thisObject](const Value *argv, int argc) {
return doCall(this, thisObject, argv, argc, context);
});
@@ -109,10 +109,6 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
: FunctionData(engine, unit)
, compiledFunction(function)
, codeData(function->code())
- , jittedCode(nullptr)
- , codeRef(nullptr)
- , aotCompiledFunction(aotFunction)
- , kind(aotFunction ? AotCompiled : JsUntyped)
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
@@ -123,7 +119,7 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable();
- bool enforceJsTypes = !aotFunction && !unit->ignoresFunctionSignature();
+ bool enforceJsTypes = !unit->ignoresFunctionSignature();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i) {
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable);
@@ -134,14 +130,24 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
nFormals = compiledFunction->nFormals;
+ if (!enforceJsTypes)
+ return;
+
+ if (aotFunction) {
+ aotCompiledCode = aotFunction->functionPtr;
+ new (&aotCompiledFunction) AOTCompiledFunction;
+ kind = AotCompiled;
+ aotCompiledFunction.types.resize(aotFunction->numArguments + 1);
+ aotFunction->signature(unit, aotCompiledFunction.types.data());
+ return;
+ }
+
// If a function has any typed arguments, but an untyped return value, the return value is void.
// If it doesn't have any arguments at all and the return value is untyped, the function is
// untyped. Users can specifically set the return type to "void" to have it enforced.
- if (!enforceJsTypes || (nFormals == 0 && !isSpecificType(compiledFunction->returnType)))
+ if (nFormals == 0 && !isSpecificType(compiledFunction->returnType))
return;
- JSTypedFunction *synthesized = new JSTypedFunction;
-
QQmlTypeLoader *typeLoader = engine->typeLoader();
auto findQmlType = [&](const CompiledData::ParameterType &param) {
@@ -151,37 +157,21 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
QV4::CompiledData::CommonType(type)));
}
- if (type == 0)
+ if (type == 0 || !typeLoader)
return QQmlType();
- const auto base = unit->baseCompilationUnit();
- const QQmlType qmltype = typeLoader
- ? base->typeNameCache->query<QQmlImport::AllowRecursion>(
- base->stringAt(type), typeLoader).type
- : QQmlType();
-
- if (!qmltype.isValid() || qmltype.isComposite())
- return qmltype;
-
- if (qmltype.isInlineComponentType()) {
- Q_ASSERT(qmltype.typeId().isValid());
-
- // If it seems to be an IC type, make sure there is an actual
- // compilation unit for it. We create inline component types speculatively.
- return QQmlMetaType::obtainCompilationUnit(qmltype.typeId())
- ? qmltype
- : QQmlType();
- }
-
+ const auto &base = unit->baseCompilationUnit();
+ const QQmlType qmltype = QQmlTypePrivate::compositeQmlType(
+ base, typeLoader, base->stringAt(type));
return qmltype.typeId().isValid() ? qmltype : QQmlType();
};
- for (quint16 i = 0; i < nFormals; ++i)
- synthesized->argumentTypes.append(findQmlType(formalsIndices[i].type));
-
- synthesized->returnType = findQmlType(compiledFunction->returnType);
- jsTypedFunction = synthesized;
+ new (&jsTypedFunction) JSTypedFunction;
kind = JsTyped;
+ jsTypedFunction.types.reserve(nFormals + 1);
+ jsTypedFunction.types.append(findQmlType(compiledFunction->returnType));
+ for (quint16 i = 0; i < nFormals; ++i)
+ jsTypedFunction.types.append(findQmlType(formalsIndices[i].type));
}
Function::~Function()
@@ -190,8 +180,18 @@ Function::~Function()
destroyFunctionTable(this, codeRef);
delete codeRef;
}
- if (kind == JsTyped)
- delete jsTypedFunction;
+
+ switch (kind) {
+ case JsTyped:
+ jsTypedFunction.~JSTypedFunction();
+ break;
+ case AotCompiled:
+ aotCompiledFunction.~AOTCompiledFunction();
+ break;
+ case JsUntyped:
+ case Eval:
+ break;
+ }
}
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 3c9617f359..7543dd3c4b 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -51,11 +51,12 @@ protected:
public:
struct JSTypedFunction {
- QList<QQmlType> argumentTypes;
- QQmlType returnType;
+ QVarLengthArray<QQmlType, 4> types;
};
- const CompiledData::Function *compiledFunction;
+ struct AOTCompiledFunction {
+ QVarLengthArray<QMetaType, 4> types;
+ };
QV4::ExecutableCompilationUnit *executableCompilationUnit() const
{
@@ -73,20 +74,28 @@ public:
ReturnedValue call(const Value *thisObject, const Value *argv, int argc,
ExecutionContext *context);
- const char *codeData;
+ const CompiledData::Function *compiledFunction = nullptr;
+ const char *codeData = nullptr;
+ JSC::MacroAssemblerCodeRef *codeRef = nullptr;
typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
- JittedCode jittedCode;
- JSC::MacroAssemblerCodeRef *codeRef;
+ typedef void (*AotCompiledCode)(const QQmlPrivate::AOTCompiledContext *context, void **argv);
+
+ union {
+ void *noFunction = nullptr;
+ JSTypedFunction jsTypedFunction;
+ AOTCompiledFunction aotCompiledFunction;
+ };
+
union {
- const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction = nullptr;
- const JSTypedFunction *jsTypedFunction;
+ JittedCode jittedCode = nullptr;
+ AotCompiledCode aotCompiledCode;
};
// first nArguments names in internalClass are the actual arguments
QV4::WriteBarrier::Pointer<Heap::InternalClass> internalClass;
int interpreterCallCount = 0;
- quint16 nFormals;
+ quint16 nFormals = 0;
enum Kind : quint8 { JsUntyped, JsTyped, AotCompiled, Eval };
Kind kind = JsUntyped;
bool detectedInjectedParameters = false;
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index b5a3934528..ab6a34435f 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -539,13 +539,13 @@ ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Va
switch (function->kind) {
case Function::AotCompiled:
return QV4::convertAndCall(
- fo->engine(), function->aotCompiledFunction, thisObject, argv, argc,
+ fo->engine(), &function->aotCompiledFunction, thisObject, argv, argc,
[fo](QObject *thisObject, void **a, const QMetaType *types, int argc) {
ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc);
});
case Function::JsTyped:
return QV4::coerceAndCall(
- fo->engine(), function->jsTypedFunction, function->compiledFunction, argv, argc,
+ fo->engine(), &function->jsTypedFunction, function->compiledFunction, argv, argc,
[fo, thisObject](const Value *argv, int argc) {
return qfoDoCall(fo, thisObject, argv, argc);
});
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index ef4b742590..ed1ca983ad 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -113,22 +113,30 @@ void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int a
template<typename Callable>
ReturnedValue convertAndCall(
- ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFunction *aotFunction,
+ ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction,
const Value *thisObject, const Value *argv, int argc, Callable call)
{
- const qsizetype numFunctionArguments = aotFunction->argumentTypes.size();
+ const qsizetype numFunctionArguments = aotFunction->types.length() - 1;
Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *));
Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType));
for (qsizetype i = 0; i < numFunctionArguments; ++i) {
- const QMetaType argumentType = aotFunction->argumentTypes[i];
+ const QMetaType argumentType = aotFunction->types[i + 1];
types[i + 1] = argumentType;
if (const qsizetype argumentSize = argumentType.sizeOf()) {
Q_ALLOCA_VAR(void, argument, argumentSize);
- if (argumentType.flags() & QMetaType::NeedsConstruction)
+ if (argumentType.flags() & QMetaType::NeedsConstruction) {
argumentType.construct(argument);
- if (i < argc)
- ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ if (i < argc)
+ ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ } else if (i >= argc
+ || !ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument)) {
+ // If we can't convert the argument, we need to default-construct it even if it
+ // doesn't formally need construction.
+ // E.g. an int doesn't need construction, but we still want it to be 0.
+ argumentType.construct(argument);
+ }
+
values[i + 1] = argument;
} else {
values[i + 1] = nullptr;
@@ -136,7 +144,7 @@ ReturnedValue convertAndCall(
}
Q_ALLOCA_DECLARE(void, returnValue);
- types[0] = aotFunction->returnType;
+ types[0] = aotFunction->types[0];
if (const qsizetype returnSize = types[0].sizeOf()) {
Q_ALLOCA_ASSIGN(void, returnValue, returnSize);
values[0] = returnValue;
@@ -201,13 +209,15 @@ bool convertAndCall(ExecutionEngine *engine, QObject *thisObject,
// Clear the return value
resultType.destruct(result);
resultType.construct(result);
- } else {
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
// When the return type is QVariant, JS objects are to be returned as
// QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately.
- if (resultType == QMetaType::fromType<QVariant>())
- *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {});
- else
- ExecutionEngine::metaTypeFromJS(jsResult, resultType, result);
+ *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {});
+ } else if (!ExecutionEngine::metaTypeFromJS(jsResult, resultType, result)) {
+ // If we cannot convert, also clear the return value.
+ // The caller may have given us an uninitialized QObject*, expecting it to be overwritten.
+ resultType.destruct(result);
+ resultType.construct(result);
}
return !jsResult->isUndefined();
}
@@ -276,7 +286,7 @@ inline ReturnedValue coerceListType(
}
if (listValueType.flags() & QMetaType::PointerToQObject) {
- QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, listValueType));
+ QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, type));
QQmlListProperty<QObject> *listProperty = newList->d()->property();
const qsizetype length = array->getLength();
@@ -402,16 +412,16 @@ ReturnedValue coerceAndCall(
{
Scope scope(engine);
- QV4::JSCallArguments jsCallData(scope, typedFunction->argumentTypes.size());
+ QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1);
const CompiledData::Parameter *formals = compiledFunction->formalsTable();
for (qsizetype i = 0; i < jsCallData.argc; ++i) {
jsCallData.args[i] = coerce(
engine, i < argc ? argv[i] : Encode::undefined(),
- typedFunction->argumentTypes[i], formals[i].type.isList());
+ typedFunction->types[i + 1], formals[i].type.isList());
}
ScopedValue result(scope, call(jsCallData.args, jsCallData.argc));
- return coerce(engine, result, typedFunction->returnType, compiledFunction->returnType.isList());
+ return coerce(engine, result, typedFunction->types[0], compiledFunction->returnType.isList());
}
// Note: \a to is unininitialized here! This is in contrast to most other related functions.
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index a834cf20d5..d78d09113a 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -988,12 +988,16 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec
Q_ASSERT(value.isObject());
Scope scope(value.as<Object>()->engine());
- ScopedArrayObject a(scope, value);
- if (a)
+ if (ScopedArrayObject a{ scope, value }) {
return toJsonArray(a, visitedObjects);
- ScopedObject o(scope, value);
- if (o)
+ } else if (Scoped<QV4::Sequence> a{ scope, value }) {
+ return toJsonArray(a, visitedObjects);
+ } else if (Scoped<QmlListWrapper> lw{ scope, value }) {
+ return toJsonArray(lw, visitedObjects);
+ } else if (ScopedObject o{ scope, value }) {
return toJsonObject(o, visitedObjects);
+ }
+
return QJsonValue(value.toQString());
}
@@ -1058,7 +1062,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso
return a.asReturnedValue();
}
-QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects)
+QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects)
{
QJsonArray result;
if (!a)
diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h
index abdfcf5d37..f6f63d7eb3 100644
--- a/src/qml/jsruntime/qv4jsonobject_p.h
+++ b/src/qml/jsruntime/qv4jsonobject_p.h
@@ -63,14 +63,13 @@ public:
{ V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); }
static inline QJsonObject toJsonObject(const QV4::Object *o)
{ V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); }
- static inline QJsonArray toJsonArray(const QV4::ArrayObject *a)
- { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); }
+ static inline QJsonArray toJsonArray(const QV4::Object *o)
+ { V4ObjectSet visitedObjects; return toJsonArray(o, visitedObjects); }
private:
static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects);
static QJsonObject toJsonObject(const Object *o, V4ObjectSet &visitedObjects);
- static QJsonArray toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects);
-
+ static QJsonArray toJsonArray(const Object *o, V4ObjectSet &visitedObjects);
};
class JsonParser
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index eea3621842..4f11d0a2ad 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -383,6 +383,56 @@ WeakValue::~WeakValue()
free();
}
+/*
+ WeakValue::set shold normally not mark objects, after all a weak value
+ is not supposed to keep an object alive.
+ However, if we are past GCState::HandleQObjectWrappers, nothing will
+ reset weak values referencing unmarked values, but those values will
+ still be swept.
+ That lead to stale pointers, and potentially to crashes. To avoid this,
+ we mark the objects here (they might still get collected in the next gc
+ run).
+ This is especially important due to the way we handle QObjectWrappers.
+ */
+void WeakValue::set(ExecutionEngine *engine, const Value &value)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ if (auto *h = value.heapObject())
+ h->mark(ms);
+ });
+ *val = value;
+}
+
+void WeakValue::set(ExecutionEngine *engine, ReturnedValue value)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ if (auto *h = QV4::Value::fromReturnedValue(value).heapObject())
+ h->mark(ms);
+ });
+
+ *val = value;
+}
+
+void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ obj->mark(ms);
+ });
+ *val = obj;
+}
+
void WeakValue::allocVal(ExecutionEngine *engine)
{
val = engine->memoryManager->m_weakValues->allocate();
diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h
index c24d337b60..d0e29a166e 100644
--- a/src/qml/jsruntime/qv4persistent_p.h
+++ b/src/qml/jsruntime/qv4persistent_p.h
@@ -131,26 +131,11 @@ public:
WeakValue &operator=(const WeakValue &other);
~WeakValue();
- void set(ExecutionEngine *engine, const Value &value)
- {
- if (!val)
- allocVal(engine);
- *val = value;
- }
+ void set(ExecutionEngine *engine, const Value &value);
- void set(ExecutionEngine *engine, ReturnedValue value)
- {
- if (!val)
- allocVal(engine);
- *val = value;
- }
+ void set(ExecutionEngine *engine, ReturnedValue value);
- void set(ExecutionEngine *engine, Heap::Base *obj)
- {
- if (!val)
- allocVal(engine);
- *val = obj;
- }
+ void set(ExecutionEngine *engine, Heap::Base *obj);
ReturnedValue value() const {
return (val ? val->asReturnedValue() : Encode::undefined());
diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp
new file mode 100644
index 0000000000..13d93c7122
--- /dev/null
+++ b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp
@@ -0,0 +1,126 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4qmetaobjectwrapper_p.h"
+
+#include <private/qqmlobjectorgadget_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QV4 {
+
+void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
+{
+ FunctionObject::init();
+ this->metaObject = metaObject;
+ constructors = nullptr;
+ constructorCount = 0;
+}
+
+void Heap::QMetaObjectWrapper::destroy()
+{
+ delete[] constructors;
+}
+
+void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+
+ const int count = metaObject->constructorCount();
+ if (constructorCount != count) {
+ delete[] constructors;
+ constructorCount = count;
+ if (count == 0) {
+ constructors = nullptr;
+ return;
+ }
+ constructors = new QQmlPropertyData[count];
+
+ for (int i = 0; i < count; ++i) {
+ QMetaMethod method = metaObject->constructor(i);
+ QQmlPropertyData &d = constructors[i];
+ d.load(method);
+ d.setCoreIndex(i);
+ }
+ }
+}
+
+
+ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
+
+ Scope scope(engine);
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ mo->init(engine);
+ return mo->asReturnedValue();
+}
+
+void QMetaObjectWrapper::init(ExecutionEngine *) {
+ const QMetaObject & mo = *d()->metaObject;
+
+ for (int i = 0; i < mo.enumeratorCount(); i++) {
+ QMetaEnum Enum = mo.enumerator(i);
+ for (int k = 0; k < Enum.keyCount(); k++) {
+ const char* key = Enum.key(k);
+ const int value = Enum.value(k);
+ defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
+ }
+ }
+}
+
+ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
+ return This->constructInternal(argv, argc);
+}
+
+ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
+{
+
+ d()->ensureConstructorsCache();
+
+ ExecutionEngine *v4 = engine();
+ const QMetaObject* mo = d()->metaObject;
+ if (d()->constructorCount == 0) {
+ return v4->throwTypeError(QLatin1String(mo->className())
+ + QLatin1String(" has no invokable constructor"));
+ }
+
+ Scope scope(v4);
+ Scoped<QObjectWrapper> object(scope);
+ JSCallData cData(nullptr, argv, argc);
+ CallData *callData = cData.callData(scope);
+
+ const QQmlObjectOrGadget objectOrGadget(mo);
+
+ if (d()->constructorCount == 1) {
+ object = QObjectMethod::callPrecise(
+ objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
+ } else if (const QQmlPropertyData *ctor = QObjectMethod::resolveOverloaded(
+ objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
+ object = QObjectMethod::callPrecise(
+ objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
+ }
+ if (object) {
+ Scoped<QMetaObjectWrapper> metaObject(scope, this);
+ object->defineDefaultProperty(v4->id_constructor(), metaObject);
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ }
+
+ return object.asReturnedValue();
+}
+
+bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
+{
+ const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
+ Q_ASSERT(aMetaObject);
+ const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
+ return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject();
+}
+
+DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
+
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h
new file mode 100644
index 0000000000..063bc089e7
--- /dev/null
+++ b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4QMETAOBJECTWRAPPER_P_H
+#define QV4QMETAOBJECTWRAPPER_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/qv4functionobject_p.h>
+#include <private/qv4value_p.h>
+
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyData;
+
+namespace QV4 {
+namespace Heap {
+
+struct QMetaObjectWrapper : FunctionObject {
+ const QMetaObject* metaObject;
+ QQmlPropertyData *constructors;
+ int constructorCount;
+
+ void init(const QMetaObject* metaObject);
+ void destroy();
+ void ensureConstructorsCache();
+};
+
+} // namespace Heap
+
+struct Q_QML_EXPORT QMetaObjectWrapper : public FunctionObject
+{
+ V4_OBJECT2(QMetaObjectWrapper, FunctionObject)
+ V4_NEEDS_DESTROY
+
+ static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
+ const QMetaObject *metaObject() const { return d()->metaObject; }
+
+protected:
+ static ReturnedValue virtualCallAsConstructor(
+ const FunctionObject *, const Value *argv, int argc, const Value *);
+ static bool virtualIsEqualTo(Managed *a, Managed *b);
+
+private:
+ void init(ExecutionEngine *engine);
+ ReturnedValue constructInternal(const Value *argv, int argc) const;
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4QMETAOBJECTWRAPPER_P_H
+
+
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index a16a9cab28..5f85aae89e 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1924,9 +1924,9 @@ static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
&& metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
}
-static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- ExecutionEngine *engine, CallData *callArgs,
- QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
+ReturnedValue QObjectMethod::callPrecise(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData &data, ExecutionEngine *engine,
+ CallData *callArgs, QMetaObject::Call callType)
{
QByteArray unknownTypeError;
@@ -1949,7 +1949,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
<< "When matching arguments for "
<< object.className() << "::" << data.name(object.metaObject()) << "():";
} else {
- const StackFrame frame = engine->stackTrace().first();
+ const StackFrame frame = stackTrace.first();
qWarning().noquote() << frame.function + QLatin1Char('@') + frame.source
+ (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
: QString());
@@ -2011,7 +2011,7 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
If two or more overloads have the same match score, return the last one. The match
score is constructed by adding the matchScore() result for each of the parameters.
*/
-static const QQmlPropertyData *ResolveOverloaded(
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
ExecutionEngine *engine, CallData *callArgs)
{
@@ -2165,7 +2165,7 @@ static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
return false;
}
-static const QQmlPropertyData *ResolveOverloaded(
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
const QQmlPropertyData *methods, int methodCount,
void **argv, int argc, const QMetaType *types)
{
@@ -2391,8 +2391,8 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
return true;
case QMetaType::QJsonArray: {
Scope scope(engine);
- ScopedArrayObject a(scope, value);
- jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(a));
+ ScopedObject o(scope, value);
+ jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
return true;
}
case QMetaType::QJsonObject: {
@@ -2944,7 +2944,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
if (d()->methodCount != 1) {
Q_ASSERT(d()->methodCount > 0);
- method = ResolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
+ method = resolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
if (method == nullptr)
return Encode::undefined();
}
@@ -2962,7 +2962,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
});
}
- return doCall([&]() { return CallPrecise(object, *method, v4, callData); });
+ return doCall([&]() { return callPrecise(object, *method, v4, callData); });
}
struct ToStringMetaMethod
@@ -3032,7 +3032,7 @@ void QObjectMethod::callInternalWithMetaTypes(
const QQmlPropertyData *method = d()->methods;
if (d()->methodCount != 1) {
Q_ASSERT(d()->methodCount > 0);
- method = ResolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
+ method = resolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
}
if (!method || method->isV4Function()) {
@@ -3084,114 +3084,6 @@ void QObjectMethod::callInternalWithMetaTypes(
DEFINE_OBJECT_VTABLE(QObjectMethod);
-
-void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
-{
- FunctionObject::init();
- this->metaObject = metaObject;
- constructors = nullptr;
- constructorCount = 0;
-}
-
-void Heap::QMetaObjectWrapper::destroy()
-{
- delete[] constructors;
-}
-
-void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
-
- const int count = metaObject->constructorCount();
- if (constructorCount != count) {
- delete[] constructors;
- constructorCount = count;
- if (count == 0) {
- constructors = nullptr;
- return;
- }
- constructors = new QQmlPropertyData[count];
-
- for (int i = 0; i < count; ++i) {
- QMetaMethod method = metaObject->constructor(i);
- QQmlPropertyData &d = constructors[i];
- d.load(method);
- d.setCoreIndex(i);
- }
- }
-}
-
-
-ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
-
- Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QMetaObjectWrapper>(metaObject)->asReturnedValue());
- mo->init(engine);
- return mo->asReturnedValue();
-}
-
-void QMetaObjectWrapper::init(ExecutionEngine *) {
- const QMetaObject & mo = *d()->metaObject;
-
- for (int i = 0; i < mo.enumeratorCount(); i++) {
- QMetaEnum Enum = mo.enumerator(i);
- for (int k = 0; k < Enum.keyCount(); k++) {
- const char* key = Enum.key(k);
- const int value = Enum.value(k);
- defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
- }
- }
-}
-
-ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
-{
- const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
- return This->constructInternal(argv, argc);
-}
-
-ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
-{
-
- d()->ensureConstructorsCache();
-
- ExecutionEngine *v4 = engine();
- const QMetaObject* mo = d()->metaObject;
- if (d()->constructorCount == 0) {
- return v4->throwTypeError(QLatin1String(mo->className())
- + QLatin1String(" has no invokable constructor"));
- }
-
- Scope scope(v4);
- Scoped<QObjectWrapper> object(scope);
- JSCallData cData(nullptr, argv, argc);
- CallData *callData = cData.callData(scope);
-
- const QQmlObjectOrGadget objectOrGadget(mo);
-
- if (d()->constructorCount == 1) {
- object = CallPrecise(objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
- } else if (const QQmlPropertyData *ctor = ResolveOverloaded(
- objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
- object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
- }
- if (object) {
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
- }
- return object.asReturnedValue();
-
-}
-
-bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
-{
- const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
- Q_ASSERT(aMetaObject);
- const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
- return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject();
-}
-
-DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
-
-
void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
{
Object::init();
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 7225612f08..1af8fc887f 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -33,6 +33,7 @@ class QObject;
class QQmlData;
class QQmlPropertyCache;
class QQmlPropertyData;
+class QQmlObjectOrGadget;
namespace QV4 {
struct QObjectSlotDispatcher;
@@ -97,16 +98,6 @@ DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) {
QV4::Heap::QObjectMethod::ThisObjectMode checkThisObject(const QMetaObject *thisMeta) const;
};
-struct QMetaObjectWrapper : FunctionObject {
- const QMetaObject* metaObject;
- QQmlPropertyData *constructors;
- int constructorCount;
-
- void init(const QMetaObject* metaObject);
- void destroy();
- void ensureConstructorsCache();
-};
-
struct QmlSignalHandler : Object {
void init(QObject *object, int signalIndex);
void destroy() {
@@ -385,24 +376,22 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
QObject *thisObject, void **argv, const QMetaType *types, int argc) const;
static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
-};
-
-struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
-{
- V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject)
- V4_NEEDS_DESTROY
+private:
+ friend struct QMetaObjectWrapper;
- static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
- const QMetaObject *metaObject() const { return d()->metaObject; }
+ static const QQmlPropertyData *resolveOverloaded(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
+ ExecutionEngine *engine, CallData *callArgs);
-protected:
- static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
- static bool virtualIsEqualTo(Managed *a, Managed *b);
+ static const QQmlPropertyData *resolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types);
-private:
- void init(ExecutionEngine *engine);
- ReturnedValue constructInternal(const Value *argv, int argc) const;
+ static ReturnedValue callPrecise(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
+ ExecutionEngine *engine, CallData *callArgs,
+ QMetaObject::Call callType = QMetaObject::InvokeMetaMethod);
};
struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp
index 637d033bd4..4ece91a2a2 100644
--- a/src/qml/jsruntime/qv4urlobject.cpp
+++ b/src/qml/jsruntime/qv4urlobject.cpp
@@ -1288,12 +1288,7 @@ ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, c
return Encode::undefined();
QList<QStringList> params = o->params();
-
- auto to_remove = std::remove_if(params.begin(), params.end(), [&name](QStringList pair) {
- return pair[0] == name;
- });
-
- params.erase(to_remove, params.end());
+ params.removeIf([&name](const auto &pair) { return pair.at(0) == name; });
o->setParams(params);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 696368fa3f..096b9a6299 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -398,16 +398,16 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
struct AOTCompiledMetaMethod
{
public:
- AOTCompiledMetaMethod(const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction)
+ AOTCompiledMetaMethod(const Function::AOTCompiledFunction *aotCompiledFunction)
: aotCompiledFunction(aotCompiledFunction)
{}
- int parameterCount() const { return aotCompiledFunction->argumentTypes.size(); }
- QMetaType returnMetaType() const { return aotCompiledFunction->returnType; }
- QMetaType parameterMetaType(int i) const { return aotCompiledFunction->argumentTypes[i]; }
+ int parameterCount() const { return aotCompiledFunction->types.size() - 1; }
+ QMetaType returnMetaType() const { return aotCompiledFunction->types[0]; }
+ QMetaType parameterMetaType(int i) const { return aotCompiledFunction->types[i + 1]; }
private:
- const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction = nullptr;
+ const Function::AOTCompiledFunction *aotCompiledFunction = nullptr;
};
void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
@@ -420,14 +420,14 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine);
Function *function = frame->v4Function;
- Q_ASSERT(function->aotCompiledFunction);
+ Q_ASSERT(function->aotCompiledCode);
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
function->compiledFunction->location.line(),
function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
- const AOTCompiledMetaMethod method(function->aotCompiledFunction);
+ const AOTCompiledMetaMethod method(&function->aotCompiledFunction);
QV4::coerceAndCall(
engine, &method, frame->returnAndArgValues(),
frame->returnAndArgTypes(), frame->argc(),
@@ -443,7 +443,7 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
aotContext.engine = engine->jsEngine();
aotContext.compilationUnit = function->executableCompilationUnit();
- function->aotCompiledFunction->functionPtr(&aotContext, argv[0], argv + 1);
+ function->aotCompiledCode(&aotContext, argv);
});
}
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 61dd59de23..8f6a6503fc 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -707,7 +707,7 @@ static constexpr int markLoopIterationCount = 1024;
bool wasDrainNecessary(MarkStack *ms, QDeadlineTimer deadline)
{
- if (ms->remainingBeforeSoftLimit() < markLoopIterationCount)
+ if (ms->remainingBeforeSoftLimit() > markLoopIterationCount)
return false;
// drain
ms->drain(deadline);
@@ -805,9 +805,14 @@ GCState initCallDestroyObjects(GCStateMachine *that, ExtraData &stateData)
stateData = GCIteratorStorage { that->mm->m_weakValues->begin() };
return CallDestroyObjects;
}
-GCState callDestroyObject(GCStateMachine *, ExtraData &stateData)
+GCState callDestroyObject(GCStateMachine *that, ExtraData &stateData)
{
PersistentValueStorage::Iterator& it = get<GCIteratorStorage>(stateData).it;
+ // destroyObject might call user code, which really shouldn't call back into the gc
+ auto oldState = std::exchange(that->mm->gcBlocked, QV4::MemoryManager::Blockness::InCriticalSection);
+ auto cleanup = qScopeGuard([&]() {
+ that->mm->gcBlocked = oldState;
+ });
// avoid repeatedly hitting the timer constantly by batching iterations
for (int i = 0; i < markLoopIterationCount; ++i) {
if (!it.p)
@@ -1235,8 +1240,9 @@ bool MemoryManager::tryForceGCCompletion()
const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
Q_ASSERT(incrementalGCIsAlreadyRunning);
auto oldTimeLimit = std::exchange(gcStateMachine->timeLimit, std::chrono::microseconds::max());
- while (gcStateMachine->inProgress())
+ while (gcStateMachine->inProgress()) {
gcStateMachine->step();
+ }
gcStateMachine->timeLimit = oldTimeLimit;
return true;
}
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index ff419b0b34..eb716671b1 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -176,6 +176,22 @@ void QQmlPrivate::qmlRegistrationWarning(
}
}
+QMetaType QQmlPrivate::compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .typeId();
+}
+
+QMetaType QQmlPrivate::compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .qListTypeId();
+}
+
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
int versionMinor, const char *qmlName,
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index f1bedaf65f..47f8e5c429 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -675,7 +675,7 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write
auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
const QV4::Function *v4Function = function();
if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
- const auto returnType = v4Function->aotCompiledFunction->returnType;
+ const auto returnType = v4Function->aotCompiledFunction.types[0];
if (returnType == QMetaType::fromType<QVariant>()) {
QVariant result;
const bool isUndefined = !evaluate(&result, returnType);
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index c6b2434146..e063418de4 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -1049,11 +1049,7 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
// filter out temporary errors as they do not really affect component's
// state (they are not part of the document compilation)
- state.errors.erase(std::remove_if(state.errors.begin(), state.errors.end(),
- [](const QQmlComponentPrivate::AnnotatedQmlError &e) {
- return e.isTransient;
- }),
- state.errors.end());
+ state.errors.removeIf([](const auto &e) { return e.isTransient; });
state.clearRequiredProperties();
if (!q->isReady()) {
@@ -1226,8 +1222,8 @@ QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
Q_ASSERT(data && data->propertyCache);
targetProp = data->propertyCache->property(targetProp->coreIndex());
}
- auto it = requiredProperties->find({createdComponent, targetProp});
- if (it != requiredProperties->end()) {
+ auto it = requiredProperties->constFind({createdComponent, targetProp});
+ if (it != requiredProperties->cend()) {
if (wasInRequiredProperties)
*wasInRequiredProperties = true;
requiredProperties->erase(it);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 4b38db9ec9..7c820679ba 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -207,8 +207,8 @@ public:
QQmlGadgetPtrWrapper *valueTypeInstance(QMetaType type)
{
int typeIndex = type.id();
- auto it = cachedValueTypeInstances.find(typeIndex);
- if (it != cachedValueTypeInstances.end())
+ auto it = cachedValueTypeInstances.constFind(typeIndex);
+ if (it != cachedValueTypeInstances.cend())
return *it;
if (QQmlValueType *valueType = QQmlMetaType::valueType(type)) {
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 7002140362..86380294ba 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -527,8 +527,8 @@ QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qml
&& (!version.hasMinorVersion()
|| (sit->version.minorVersion() <= version.minorVersion()))) {
// Load the highest version that matches
- QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
- if (vit == versioned.end()
+ const auto vit = versioned.constFind(sit->nameSpace);
+ if (vit == versioned.cend()
|| (vit->version.minorVersion() < sit->version.minorVersion())) {
versioned.insert(sit->nameSpace, *sit);
}
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index a21c225eba..1175bde3db 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -668,10 +668,10 @@ QQmlType QQmlMetaType::findCompositeType(
QQmlMetaTypeDataPtr data;
bool urlExists = true;
- auto found = data->urlToType.find(normalized);
- if (found == data->urlToType.end()) {
- found = data->urlToNonFileImportType.find(normalized);
- if (found == data->urlToNonFileImportType.end())
+ auto found = data->urlToType.constFind(normalized);
+ if (found == data->urlToType.cend()) {
+ found = data->urlToNonFileImportType.constFind(normalized);
+ if (found == data->urlToNonFileImportType.cend())
urlExists = false;
}
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index ef03b1909e..8b1c251e2b 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -165,11 +165,9 @@ public:
void removePendingBinding(QObject *target, int propertyIndex)
{
QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
- auto it = std::remove_if(pendingBindings.begin(), pendingBindings.end(),
- [&](const DeferredQPropertyBinding &deferred) {
+ pendingBindings.removeIf([&](const DeferredQPropertyBinding &deferred) {
return deferred.properyIndex == propertyIndex && deferred.target == target;
});
- pendingBindings.erase(it, pendingBindings.end());
}
private:
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index f5e1feb4f2..92c9765509 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -740,10 +740,10 @@ namespace QQmlPrivate
};
struct AOTCompiledFunction {
- qintptr extraData;
- QMetaType returnType;
- QList<QMetaType> argumentTypes;
- void (*functionPtr)(const AOTCompiledContext *context, void *resultPtr, void **arguments);
+ int functionIndex;
+ int numArguments;
+ void (*signature)(QV4::ExecutableCompilationUnit *unit, QMetaType *argTypes);
+ void (*functionPtr)(const AOTCompiledContext *context, void **argv);
};
#if QT_DEPRECATED_SINCE(6, 6)
@@ -1152,6 +1152,11 @@ namespace QQmlPrivate
Q_QML_EXPORT void qmlRegistrationWarning(QmlRegistrationWarning warning, QMetaType type);
+ Q_QML_EXPORT QMetaType compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+ Q_QML_EXPORT QMetaType compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+
} // namespace QQmlPrivate
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index f6efe8d358..06b405c7e4 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -5,67 +5,10 @@
#include <private/qqmlengine_p.h>
-#if QT_CONFIG(regularexpression)
-#include <QtCore/qregularexpression.h>
-#endif
-
QT_BEGIN_NAMESPACE
QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
-
-QMetaType QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::CommonType type)
-{
- switch (type) {
- case QV4::CompiledData::CommonType::Void: return QMetaType();
- case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
- case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
- case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
- case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
- case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
- case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
- case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
- case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
- case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
-#if QT_CONFIG(regularexpression)
- case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
-#else
- case QV4::CompiledData::CommonType::RegExp: return QMetaType();
-#endif
- case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
- case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
- case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
- case QV4::CompiledData::CommonType::Invalid: break;
- };
- return QMetaType {};
-}
-
-QMetaType QQmlPropertyCacheCreatorBase::listTypeForPropertyType(QV4::CompiledData::CommonType type)
-{
- switch (type) {
- case QV4::CompiledData::CommonType::Void: return QMetaType();
- case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
- case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
- case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
- case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
- case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
- case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
- case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
- case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
- case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
-#if QT_CONFIG(regularexpression)
- case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
-#else
- case QV4::CompiledData::CommonType::RegExp: return QMetaType();
-#endif
- case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
- case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
- case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
- case QV4::CompiledData::CommonType::Invalid: break;
- };
- return QMetaType {};
-}
-
template<typename BaseNameHandler, typename FailHandler>
auto processUrlForClassName(
const QUrl &url, BaseNameHandler &&baseNameHandler, FailHandler &&failHandler)
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 93987a1123..4d49ca6ed4 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -24,6 +24,11 @@
#include <private/qqmlsignalnames_p.h>
#include <QScopedValueRollback>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
#include <vector>
QT_BEGIN_NAMESPACE
@@ -67,8 +72,57 @@ struct QQmlPropertyCacheCreatorBase
public:
static QAtomicInt Q_AUTOTEST_EXPORT classIndexCounter;
- static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type);
- static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type);
+ static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+
+ static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
static bool canCreateClassNameTypeByUrl(const QUrl &url);
static QByteArray createClassNameTypeByUrl(const QUrl &url);
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index ffffd0fdd9..622e91a4b2 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -51,10 +51,6 @@ public:
explicit QQmlType(const QQmlTypePrivate *priv);
~QQmlType();
- bool operator ==(const QQmlType &other) const {
- return d.data() == other.d.data();
- }
-
bool isValid() const { return !d.isNull(); }
QByteArray typeName() const;
@@ -176,6 +172,15 @@ public:
private:
friend class QQmlTypePrivate;
friend size_t qHash(const QQmlType &t, size_t seed);
+ friend bool operator==(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return a.d.data() == b.d.data();
+ }
+ friend bool operator!=(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return !(a == b);
+ }
+
QQmlRefPointer<const QQmlTypePrivate> d;
};
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 8f614eeac2..2bf83ddb8b 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -21,6 +21,9 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4engine_p.h>
#include <QAtomicInteger>
@@ -231,6 +234,27 @@ public:
return nullptr;
}
+ static QQmlType compositeQmlType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit,
+ QQmlTypeLoader *typeLoader, const QString &type)
+ {
+ Q_ASSERT(typeLoader);
+
+ const QQmlType qmltype
+ = unit->typeNameCache->query<QQmlImport::AllowRecursion>(type, typeLoader).type;
+ if (!qmltype.isValid())
+ return qmltype;
+
+ if (qmltype.isInlineComponentType()
+ && !QQmlMetaType::obtainCompilationUnit(qmltype.typeId())) {
+ // If it seems to be an IC type, make sure there is an actual
+ // compilation unit for it. We create inline component types speculatively.
+ return QQmlType();
+ }
+
+ return qmltype;
+ }
+
private:
mutable QAtomicPointer<const ProxyMetaObjects> proxyMetaObjects;
mutable QAtomicPointer<const Enums> enums;
diff --git a/src/qml/qqmlbuiltins_p.h b/src/qml/qqmlbuiltins_p.h
index b4dff0d81f..edf2579760 100644
--- a/src/qml/qqmlbuiltins_p.h
+++ b/src/qml/qqmlbuiltins_p.h
@@ -36,15 +36,53 @@
#include <QtCore/qtypes.h>
#include <QtCore/qchar.h>
+#include <climits>
+
#if QT_CONFIG(regularexpression)
#include <QtCore/qregularexpression.h>
#endif
QT_BEGIN_NAMESPACE
+// moc doesn't do 64bit constants, so we have to determine the size of qsizetype indirectly.
+// We assume that qsizetype is always the same size as a pointer. I haven't seen a platform
+// where this is not the case.
+// Furthermore moc is wrong about pretty much everything on 64bit windows. We need to hardcode
+// the size there.
+// Likewise, we also have to determine the size of long and ulong indirectly.
+
+#if defined(Q_OS_WIN64)
+
+static_assert(sizeof(long) == 4);
+#define QML_LONG_IS_32BIT
+static_assert(sizeof(qsizetype) == 8);
+#define QML_SIZE_IS_64BIT
+
+#elif QT_POINTER_SIZE == 4
+
+static_assert(sizeof(long) == 4);
+#define QML_LONG_IS_32BIT
+static_assert(sizeof(qsizetype) == 4);
+#define QML_SIZE_IS_32BIT
+
+#else
+
+static_assert(sizeof(long) == 8);
+#define QML_LONG_IS_64BIT
+static_assert(sizeof(qsizetype) == 8);
+#define QML_SIZE_IS_64BIT
+
+#endif
+
#define QML_EXTENDED_JAVASCRIPT(EXTENDED_TYPE) \
Q_CLASSINFO("QML.Extended", #EXTENDED_TYPE) \
- Q_CLASSINFO("QML.ExtensionIsJavaScript", "true") \
+ Q_CLASSINFO("QML.ExtensionIsJavaScript", "true")
+
+template<typename A> struct QQmlPrimitiveAliasFriend {};
+
+#define QML_PRIMITIVE_ALIAS(PRIMITIVE_ALIAS) \
+ Q_CLASSINFO("QML.PrimitiveAlias", #PRIMITIVE_ALIAS) \
+ friend QQmlPrimitiveAliasFriend<PRIMITIVE_ALIAS>;
struct QQmlVoidForeign
{
@@ -81,6 +119,14 @@ struct QQmlIntForeign
QML_VALUE_TYPE(int)
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(int)
+ QML_PRIMITIVE_ALIAS(qint32)
+ QML_PRIMITIVE_ALIAS(int32_t)
+#ifdef QML_SIZE_IS_32BIT
+ QML_PRIMITIVE_ALIAS(qsizetype)
+#endif
+#ifdef QML_LONG_IS_32BIT
+ QML_PRIMITIVE_ALIAS(long)
+#endif
};
struct QQmlDoubleForeign
@@ -90,6 +136,9 @@ struct QQmlDoubleForeign
QML_VALUE_TYPE(double)
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(double)
+#if !defined(QT_COORD_TYPE) || defined(QT_COORD_TYPE_IS_DOUBLE)
+ QML_PRIMITIVE_ALIAS(qreal)
+#endif
};
struct QQmlStringForeign
@@ -164,6 +213,10 @@ struct QQmlQint8Foreign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(qint8)
+ QML_PRIMITIVE_ALIAS(int8_t)
+#if CHAR_MAX == SCHAR_MAX
+ QML_PRIMITIVE_ALIAS(char)
+#endif
};
struct QQmlQuint8Foreign
@@ -172,14 +225,11 @@ struct QQmlQuint8Foreign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(quint8)
-};
-
-struct QQmlCharForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED_JAVASCRIPT(Number)
- QML_FOREIGN(char)
+ QML_PRIMITIVE_ALIAS(uint8_t)
+ QML_PRIMITIVE_ALIAS(uchar)
+#if CHAR_MAX == UCHAR_MAX
+ QML_PRIMITIVE_ALIAS(char)
+#endif
};
struct QQmlShortForeign
@@ -188,6 +238,8 @@ struct QQmlShortForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(short)
+ QML_PRIMITIVE_ALIAS(qint16)
+ QML_PRIMITIVE_ALIAS(int16_t)
};
struct QQmlUshortForeign
@@ -196,22 +248,8 @@ struct QQmlUshortForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(ushort)
-};
-
-struct QQmlLongForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED_JAVASCRIPT(Number)
- QML_FOREIGN(long)
-};
-
-struct QQmlUlongForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED_JAVASCRIPT(Number)
- QML_FOREIGN(ulong)
+ QML_PRIMITIVE_ALIAS(quint16)
+ QML_PRIMITIVE_ALIAS(uint16_t)
};
struct QQmlUintForeign
@@ -220,6 +258,11 @@ struct QQmlUintForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(uint)
+ QML_PRIMITIVE_ALIAS(quint32)
+ QML_PRIMITIVE_ALIAS(uint32_t)
+#ifdef QML_LONG_IS_32BIT
+ QML_PRIMITIVE_ALIAS(ulong)
+#endif
};
struct QQmlQlonglongForeign
@@ -228,6 +271,14 @@ struct QQmlQlonglongForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(qlonglong)
+ QML_PRIMITIVE_ALIAS(qint64)
+ QML_PRIMITIVE_ALIAS(int64_t)
+#ifdef QML_LONG_IS_64BIT
+ QML_PRIMITIVE_ALIAS(long)
+#endif
+#ifdef QML_SIZE_IS_64BIT
+ QML_PRIMITIVE_ALIAS(qsizetype)
+#endif
};
struct QQmlQulonglongForeign
@@ -236,6 +287,11 @@ struct QQmlQulonglongForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(qulonglong)
+ QML_PRIMITIVE_ALIAS(quint64)
+ QML_PRIMITIVE_ALIAS(uint64_t)
+#ifdef QML_LONG_IS_64BIT
+ QML_PRIMITIVE_ALIAS(ulong)
+#endif
};
struct QQmlFloatForeign
@@ -244,6 +300,9 @@ struct QQmlFloatForeign
QML_ANONYMOUS
QML_EXTENDED_JAVASCRIPT(Number)
QML_FOREIGN(float)
+#if defined(QT_COORD_TYPE) && defined(QT_COORD_TYPE_IS_FLOAT)
+ QML_PRIMITIVE_ALIAS(qreal)
+#endif
};
struct QQmlQCharForeign
@@ -299,7 +358,8 @@ struct QQmlQObjectListForeign
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QObjectList)
- QML_SEQUENTIAL_CONTAINER(QObject *)
+ QML_SEQUENTIAL_CONTAINER(QObject*)
+ QML_PRIMITIVE_ALIAS(QList<QObject*>)
};
struct QQmlQJSValueForeign
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 22a1030afd..4e7d1cbf1c 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -25,6 +25,7 @@ qt_internal_add_module(QmlCompiler
qqmljslogger_p.h qqmljslogger.cpp
qqmljsloggingutils.h qqmljsloggingutils.cpp qqmljsloggingutils_p.h
qqmljsmetatypes_p.h qqmljsmetatypes.cpp
+ qqmljsoptimizations_p.h qqmljsoptimizations.cpp
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
qqmljsscope.cpp qqmljsscope_p.h
@@ -43,6 +44,8 @@ qt_internal_add_module(QmlCompiler
qqmlsa_p.h qqmlsa.h qqmlsa.cpp
qqmlsaconstants.h
qqmlsasourcelocation.h qqmlsasourcelocation.cpp qqmlsasourcelocation_p.h
+ NO_UNITY_BUILD_SOURCES
+ qqmljsoptimizations.cpp
PUBLIC_LIBRARIES
Qt::Core
Qt::Qml
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
index 67d331e9ff..392a8b9ba7 100644
--- a/src/qmlcompiler/qqmljsbasicblocks.cpp
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qqmljsbasicblocks_p.h"
+#include "qqmljsutils_p.h"
#include <QtQml/private/qv4instr_moth_p.h>
@@ -101,24 +102,14 @@ void QQmlJSBasicBlocks::dumpDOTGraph()
}
}
-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());
-}
-
-QQmlJSCompilePass::InstructionAnnotations
-QQmlJSBasicBlocks::run(const Function *function, const InstructionAnnotations &annotations,
- QQmlJS::DiagnosticMessage *error, QQmlJSAotCompiler::Flags compileFlags,
+QQmlJSCompilePass::BlocksAndAnnotations
+QQmlJSBasicBlocks::run(const Function *function, QQmlJSAotCompiler::Flags compileFlags,
bool &basicBlocksValidationFailed)
{
- m_function = function;
- m_annotations = annotations;
- m_error = error;
basicBlocksValidationFailed = false;
+ m_function = function;
+
for (int i = 0, end = function->argumentTypes.size(); i != end; ++i) {
InstructionAnnotation annotation;
annotation.changedRegisterIndex = FirstArgument + i;
@@ -157,7 +148,7 @@ QQmlJSBasicBlocks::run(const Function *function, const InstructionAnnotations &a
reset();
decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
- deduplicate(it->second.jumpOrigins);
+ QQmlJSUtils::deduplicate(it->second.jumpOrigins);
}
if (compileFlags.testFlag(QQmlJSAotCompiler::ValidateBasicBlocks) || qv4ValidateBasicBlocks()) {
@@ -167,16 +158,12 @@ QQmlJSBasicBlocks::run(const Function *function, const InstructionAnnotations &a
}
}
- populateBasicBlocks();
- populateReaderLocations();
- adjustTypes();
-
if (qv4DumpBasicBlocks()) {
dumpBasicBlocks();
dumpDOTGraph();
}
- return std::move(m_annotations);
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type)
@@ -301,246 +288,8 @@ void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
m_basicBlocks.insert(nextInstructionOffset(), BasicBlock());
}
-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;
-};
-
-struct PendingBlock
-{
- QQmlJSBasicBlocks::Conversions conversions;
- int start = -1;
- bool registerActive = false;
-};
-
-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->typeReaders.isEmpty()
- || reader->registerReadersAndConversions.isEmpty())) {
-
- if (it->second.isRename) {
- // If it's a rename, it doesn't "own" its output type. The type may
- // still be read elsewhere, even if this register isn't. However, we're
- // not interested in the variant or any other details of the register.
- // Therefore just delete it.
- it->second.changedRegisterIndex = InvalidRegister;
- it->second.changedRegister = QQmlJSRegisterContent();
- } else {
- // void the output, rather than deleting it. We still need its variant.
- bool adjusted = m_typeResolver->adjustTrackedType(
- it->second.changedRegister.storedType(),
- m_typeResolver->voidType());
- Q_ASSERT(adjusted); // Can always convert to void
-
- adjusted = m_typeResolver->adjustTrackedType(
- m_typeResolver->containedType(it->second.changedRegister),
- m_typeResolver->voidType());
- Q_ASSERT(adjusted); // Can always convert to void
- }
- m_readerLocations.erase(reader);
-
- // If it's not a label and has no side effects, we can drop the instruction.
- if (!it->second.hasSideEffects) {
- if (!it->second.readRegisters.isEmpty()) {
- it->second.readRegisters.clear();
- erasedReaders = true;
- }
- if (m_basicBlocks.find(it.key()) == m_basicBlocks.end())
- 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()];
- access.trackedRegister = writtenRegister;
- 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 = basicBlockForInstruction(m_basicBlocks, writeIt.key());
- QList<PendingBlock> blocks = { { {}, blockIt->first, true } };
- QHash<int, PendingBlock> processedBlocks;
- bool isFirstBlock = true;
-
- while (!blocks.isEmpty()) {
- const PendingBlock block = blocks.takeLast();
-
- // 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)
- processedBlocks.insert(block.start, block);
-
- auto nextBlock = m_basicBlocks.find(block.start);
- auto currentBlock = nextBlock++;
- bool registerActive = block.registerActive;
- Conversions conversions = block.conversions;
-
- 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) {
- if (registerActive
- && blockInstr->second.typeConversions.contains(writtenRegister)) {
- conversions.insert(blockInstr.key());
- }
-
- for (auto readIt = blockInstr->second.readRegisters.constBegin(),
- end = blockInstr->second.readRegisters.constEnd();
- readIt != end; ++readIt) {
- if (!blockInstr->second.isRename && containsAny(
- readIt->second.content.conversionOrigins(), access.trackedTypes)) {
- Q_ASSERT(readIt->second.content.isConversion());
- Q_ASSERT(readIt->second.content.conversionResult());
- access.typeReaders[blockInstr.key()]
- = readIt->second.content.conversionResult();
- }
- if (registerActive && readIt->first == writtenRegister)
- access.registerReadersAndConversions[blockInstr.key()] = conversions;
- }
-
- if (blockInstr->second.changedRegisterIndex == writtenRegister) {
- conversions.clear();
- registerActive = false;
- }
- }
-
- auto scheduleBlock = [&](int blockStart) {
- // If we find that an already processed block has the register activated by this jump,
- // we need to re-evaluate it. We also need to propagate any newly found conversions.
- const auto processed = processedBlocks.find(blockStart);
- if (processed == processedBlocks.end()) {
- blocks.append({conversions, blockStart, registerActive});
- } else if (registerActive && !processed->registerActive) {
- blocks.append({conversions, blockStart, registerActive});
- } else {
-
- // TODO: Use unite() once it is fixed.
- // We don't use unite() here since it would be more expensive. unite()
- // effectively loops on only insert() and insert() does a number of checks
- // each time. We trade those checks for calculating the hash twice on each
- // iteration. Calculating the hash is very cheap for integers.
- Conversions merged = processed->conversions;
- for (const int conversion : std::as_const(conversions)) {
- if (!merged.contains(conversion))
- merged.insert(conversion);
- }
-
- if (merged.size() > processed->conversions.size())
- blocks.append({std::move(merged), blockStart, registerActive});
- }
- };
-
- if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
- scheduleBlock(nextBlock->first);
-
- const int jumpTarget = currentBlock->second.jumpTarget;
- if (jumpTarget != -1)
- scheduleBlock(jumpTarget);
-
- if (isFirstBlock)
- isFirstBlock = false;
- }
-
- 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 typeIt = readers->typeReaders.begin();
- typeIt != readers->typeReaders.end();) {
- if (m_annotations.contains(typeIt.key()))
- ++typeIt;
- else
- typeIt = readers->typeReaders.erase(typeIt);
- }
-
- for (auto registerIt = readers->registerReadersAndConversions.begin();
- registerIt != readers->registerReadersAndConversions.end();) {
- if (m_annotations.contains(registerIt.key()))
- ++registerIt;
- else
- registerIt = readers->registerReadersAndConversions.erase(registerIt);
- }
- }
-
- if (!eraseDeadStore(it))
- newAnnotations.appendOrdered(it);
- }
-
- m_annotations = newAnnotations.take();
- }
-}
-
-QFlatMap<int, QQmlJSBasicBlocks::BasicBlock>::iterator
-QQmlJSBasicBlocks::basicBlockForInstruction(QFlatMap<int, BasicBlock> &container,
- int instructionOffset)
+QQmlJSCompilePass::BasicBlocks::iterator QQmlJSBasicBlocks::basicBlockForInstruction(
+ QFlatMap<int, BasicBlock> &container, int instructionOffset)
{
auto block = container.lower_bound(instructionOffset);
if (block == container.end() || block->first != instructionOffset)
@@ -548,278 +297,10 @@ QQmlJSBasicBlocks::basicBlockForInstruction(QFlatMap<int, BasicBlock> &container
return block;
}
-QFlatMap<int, QQmlJSBasicBlocks::BasicBlock>::const_iterator
-QQmlJSBasicBlocks::basicBlockForInstruction(const QFlatMap<int, BasicBlock> &container,
- int instructionOffset) const
-{
- auto *nonConstThis = const_cast<QQmlJSBasicBlocks *>(this);
- return nonConstThis->basicBlockForInstruction(
- const_cast<QFlatMap<int, BasicBlock> &>(container), instructionOffset);
-}
-
-bool QQmlJSBasicBlocks::canMove(int instructionOffset, const RegisterAccess &access) const
-{
- if (access.registerReadersAndConversions.size() != 1)
- return false;
- return basicBlockForInstruction(m_basicBlocks, instructionOffset)
- == basicBlockForInstruction(m_basicBlocks, access.registerReadersAndConversions.begin().key());
-}
-
-static QString adjustErrorMessage(
- const QQmlJSScope::ConstPtr &origin, const QQmlJSScope::ConstPtr &conversion) {
- return QLatin1String("Cannot convert from ")
- + origin->internalName() + QLatin1String(" to ") + conversion->internalName();
-}
-
-static QString adjustErrorMessage(
- const QQmlJSScope::ConstPtr &origin, const QList<QQmlJSScope::ConstPtr> &conversions) {
- if (conversions.size() == 1)
- return adjustErrorMessage(origin, conversions[0]);
-
- QString types;
- for (const QQmlJSScope::ConstPtr &type : conversions) {
- if (!types.isEmpty())
- types += QLatin1String(", ");
- types += type->internalName();
- }
- return QLatin1String("Cannot convert from ")
- + origin->internalName() + QLatin1String(" to union of ") + types;
-}
-
-void QQmlJSBasicBlocks::adjustTypes()
+QQmlJSCompilePass::BasicBlocks::const_iterator QQmlJSBasicBlocks::basicBlockForInstruction(
+ const BasicBlocks &container, int instructionOffset)
{
- using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>;
-
- QHash<int, QList<int>> liveConversions;
- QHash<int, QList<int>> movableReads;
-
- const auto handleRegisterReadersAndConversions
- = [&](QHash<int, RegisterAccess>::const_iterator it) {
- for (auto conversions = it->registerReadersAndConversions.constBegin(),
- end = it->registerReadersAndConversions.constEnd(); conversions != end;
- ++conversions) {
- if (conversions->isEmpty() && canMove(it.key(), it.value()))
- movableReads[conversions.key()].append(it->trackedRegister);
- for (int conversion : *conversions)
- liveConversions[conversion].append(it->trackedRegister);
- }
- };
-
- const auto transformRegister = [&](const QQmlJSRegisterContent &content) {
- const QQmlJSScope::ConstPtr conversion
- = m_typeResolver->storedType(m_typeResolver->containedType(content));
- if (!m_typeResolver->adjustTrackedType(content.storedType(), conversion))
- setError(adjustErrorMessage(content.storedType(), conversion));
- };
-
- // Handle the array definitions first.
- // Changing the array type changes the expected element types.
- auto adjustArray = [&](int instructionOffset, int mode) {
- auto it = m_readerLocations.find(instructionOffset);
- if (it == m_readerLocations.end())
- return;
-
- const InstructionAnnotation &annotation = m_annotations[instructionOffset];
- if (annotation.readRegisters.isEmpty())
- return;
-
- Q_ASSERT(it->trackedTypes.size() == 1);
- Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
-
- if (it->trackedTypes[0]->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
- return; // Constructed something else.
-
- if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
- setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
-
- // Now we don't adjust the type we store, but rather the type we expect to read. We
- // can do this because we've tracked the read type when we defined the array in
- // QQmlJSTypePropagator.
- if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
- const QQmlJSRegisterContent content = annotation.readRegisters.begin().value().content;
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
-
- // If it's the 1-arg Array ctor, and the argument is a number, that's special.
- if (mode != ObjectOrArrayDefinition::ArrayConstruct1ArgId
- || !m_typeResolver->equals(contained, m_typeResolver->realType())) {
- if (!m_typeResolver->adjustTrackedType(contained, valueType))
- setError(adjustErrorMessage(contained, valueType));
-
- // We still need to adjust the stored type, too.
- transformRegister(content);
- }
- }
-
- handleRegisterReadersAndConversions(it);
- m_readerLocations.erase(it);
- };
-
- // Handle the object definitions.
- // Changing the object type changes the expected property types.
- const auto adjustObject = [&](const ObjectOrArrayDefinition &object) {
- auto it = m_readerLocations.find(object.instructionOffset);
- if (it == m_readerLocations.end())
- return;
-
- const InstructionAnnotation &annotation = m_annotations[object.instructionOffset];
-
- Q_ASSERT(it->trackedTypes.size() == 1);
- QQmlJSScope::ConstPtr resultType = it->trackedTypes[0];
-
- Q_ASSERT(resultType == m_typeResolver->containedType(annotation.changedRegister));
- Q_ASSERT(!annotation.readRegisters.isEmpty());
-
- if (!m_typeResolver->adjustTrackedType(resultType, it->typeReaders.values()))
- setError(adjustErrorMessage(resultType, it->typeReaders.values()));
-
- if (m_typeResolver->equals(resultType, m_typeResolver->varType())
- || m_typeResolver->equals(resultType, m_typeResolver->variantMapType())) {
- // It's all variant anyway
- return;
- }
-
- const int classSize = m_jsUnitGenerator->jsClassSize(object.internalClassId);
- Q_ASSERT(object.argc >= classSize);
-
- for (int i = 0; i < classSize; ++i) {
- // Now we don't adjust the type we store, but rather the types we expect to read. We
- // can do this because we've tracked the read types when we defined the object in
- // QQmlJSTypePropagator.
-
- const QString propName = m_jsUnitGenerator->jsClassMember(object.internalClassId, i);
- const QQmlJSMetaProperty property = resultType->property(propName);
- if (!property.isValid()) {
- setError(
- resultType->internalName()
- + QLatin1String(" has no property called ")
- + propName);
- continue;
- }
- const QQmlJSScope::ConstPtr propType = property.type();
- if (propType.isNull()) {
- setError(QLatin1String("Cannot resolve type of property ") + propName);
- continue;
- }
- const QQmlJSRegisterContent content = annotation.readRegisters[object.argv + i].content;
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
- if (!m_typeResolver->adjustTrackedType(contained, propType))
- setError(adjustErrorMessage(contained, propType));
-
- // We still need to adjust the stored type, too.
- transformRegister(content);
- }
-
- // The others cannot be adjusted. We don't know their names, yet.
- // But we might still be able to use the variants.
- };
-
- // Iterate in reverse so that we can have nested lists and objects and the types are propagated
- // from the outer lists/objects to the inner ones.
- for (auto it = m_objectAndArrayDefinitions.crbegin(), end = m_objectAndArrayDefinitions.crend();
- it != end; ++it) {
- switch (it->internalClassId) {
- case ObjectOrArrayDefinition::ArrayClassId:
- case ObjectOrArrayDefinition::ArrayConstruct1ArgId:
- adjustArray(it->instructionOffset, it->internalClassId);
- break;
- default:
- adjustObject(*it);
- break;
- }
- }
-
- for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
- handleRegisterReadersAndConversions(it);
-
- // There is always one first occurrence of any tracked type. Conversions don't change
- // the type.
- if (it->trackedTypes.size() != 1)
- continue;
-
- // Don't adjust renamed values. We only adjust the originals.
- const int writeLocation = it.key();
- if (writeLocation >= 0 && m_annotations[writeLocation].isRename)
- continue;
-
- if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
- setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
- }
-
-
-
- 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 conversion = i->second.typeConversions.begin(),
- conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd;
- ++conversion) {
- if (!liveConversions[i.key()].contains(conversion.key()))
- continue;
-
- QQmlJSScope::ConstPtr newResult;
- const auto content = conversion->second.content;
- if (content.isConversion()) {
- QQmlJSScope::ConstPtr conversionResult = content.conversionResult();
- const auto conversionOrigins = content.conversionOrigins();
- for (const auto &origin : conversionOrigins)
- newResult = m_typeResolver->merge(newResult, origin);
- if (!m_typeResolver->adjustTrackedType(conversionResult, newResult))
- setError(adjustErrorMessage(conversionResult, newResult));
- }
- transformRegister(content);
- newRegisters.appendOrdered(conversion);
- }
- i->second.typeConversions = newRegisters.take();
-
- for (int movable : std::as_const(movableReads[i.key()]))
- i->second.readRegisters[movable].canMove = true;
- }
-}
-
-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> writtenTypes;
- QList<int> 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) {
- if (!instruction.isRename) {
- Q_ASSERT(it->second.content.isConversion());
- for (const QQmlJSScope::ConstPtr &origin :
- it->second.content.conversionOrigins()) {
- if (!writtenTypes.contains(origin))
- block.readTypes.append(origin);
- }
- }
- if (!writtenRegisters.contains(it->first))
- block.readRegisters.append(it->first);
- }
-
- // If it's just a renaming, the type has existed in a different register before.
- if (instruction.changedRegisterIndex != InvalidRegister) {
- if (!instruction.isRename) {
- writtenTypes.append(m_typeResolver->trackedContainedType(
- instruction.changedRegister));
- }
- writtenRegisters.append(instruction.changedRegisterIndex);
- }
- }
-
- deduplicate(block.readTypes);
- deduplicate(block.readRegisters);
- }
+ return basicBlockForInstruction(const_cast<BasicBlocks &>(container), instructionOffset);
}
QQmlJSBasicBlocks::BasicBlocksValidationResult QQmlJSBasicBlocks::basicBlocksValidation()
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
index 346c91b951..a443d422ea 100644
--- a/src/qmlcompiler/qqmljsbasicblocks_p.h
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -24,18 +24,6 @@ QT_BEGIN_NAMESPACE
class Q_QMLCOMPILER_EXPORT QQmlJSBasicBlocks : public QQmlJSCompilePass
{
public:
- using Conversions = QSet<int>;
-
- struct BasicBlock {
- QList<int> jumpOrigins;
- QList<int> readRegisters;
- QList<QQmlJSScope::ConstPtr> readTypes;
- int jumpTarget = -1;
- bool jumpIsUnconditional = false;
- bool isReturnBlock = false;
- bool isThrowBlock = false;
- };
-
QQmlJSBasicBlocks(const QV4::Compiler::Context *context,
const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
@@ -45,35 +33,21 @@ public:
~QQmlJSBasicBlocks() = default;
- InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations,
- QQmlJS::DiagnosticMessage *error, QQmlJSAotCompiler::Flags,
- bool &basicBlocksValidationFailed);
+ QQmlJSCompilePass::BlocksAndAnnotations run(const Function *function,
+ QQmlJSAotCompiler::Flags compileFlags,
+ bool &basicBlocksValidationFailed);
struct BasicBlocksValidationResult { bool success = true; QString errorMessage; };
BasicBlocksValidationResult basicBlocksValidation();
-private:
- struct RegisterAccess
- {
- QList<QQmlJSScope::ConstPtr> trackedTypes;
- QHash<int, QQmlJSScope::ConstPtr> typeReaders;
- QHash<int, Conversions> registerReadersAndConversions;
- int trackedRegister;
- };
-
- struct ObjectOrArrayDefinition
- {
- enum {
- ArrayClassId = -1,
- ArrayConstruct1ArgId = -2,
- };
+ static BasicBlocks::iterator
+ basicBlockForInstruction(QFlatMap<int, BasicBlock> &container, int instructionOffset);
+ static BasicBlocks::const_iterator
+ basicBlockForInstruction(const QFlatMap<int, BasicBlock> &container, int instructionOffset);
- int instructionOffset = -1;
- int internalClassId = ArrayClassId;
- int argc = 0;
- int argv = -1;
- };
+ QList<ObjectOrArrayDefinition> objectAndArrayDefinitions() const;
+private:
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override;
void endInstruction(QV4::Moth::Instr::Type type) override;
@@ -94,23 +68,11 @@ private:
enum JumpMode { Unconditional, Conditional };
void processJump(int offset, JumpMode mode);
- void populateBasicBlocks();
- void populateReaderLocations();
- void adjustTypes();
- bool canMove(int instructionOffset, const RegisterAccess &access) const;
-
- QFlatMap<int, BasicBlock>::iterator
- basicBlockForInstruction(QFlatMap<int, BasicBlock> &container, int instructionOffset);
- QFlatMap<int, BasicBlock>::const_iterator
- basicBlockForInstruction(const QFlatMap<int, BasicBlock> &container, int instructionOffset) const;
void dumpBasicBlocks();
void dumpDOTGraph();
const QV4::Compiler::Context *m_context;
- InstructionAnnotations m_annotations;
- QFlatMap<int, BasicBlock> m_basicBlocks;
- QHash<int, RegisterAccess> m_readerLocations;
QList<ObjectOrArrayDefinition> m_objectAndArrayDefinitions;
bool m_skipUntilNextLabel = false;
bool m_hadBackJumps = false;
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index d75c48232a..a3ac32f024 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -53,10 +53,11 @@ QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) c
}
QQmlJSCodeGenerator::QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
- const QV4::Compiler::JSUnitGenerator *unitGenerator,
- const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver,
+ QQmlJSLogger *logger, BasicBlocks basicBlocks,
+ InstructionAnnotations annotations)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
, m_context(compilerContext)
{}
@@ -72,33 +73,63 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
+ u"\"); return t; }()"_s;
}
+QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeListMetaType("
+ "aotContext->compilationUnit, \""_s
+ + elementName
+ + u"\"); return t; }(aotContext)"_s;
+}
+
+QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeMetaType("
+ "aotContext->compilationUnit, QStringLiteral(\""_s
+ + elementName
+ + u"\")); return t; }(aotContext)"_s;
+}
+
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite()) {
- if (objectType->internalName() == u"QObject"_s
- || objectType->internalName() == u"QQmlComponent"_s) {
- return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ if (objectType->isComposite()) {
+ const QString name = m_typeResolver->nameForType(objectType);
+ if (name.isEmpty()) {
+ reject(u"retrieving the metaObject of a composite type without an element name."_s);
+ return QString();
}
- return metaTypeFromName(objectType) + u".metaObject()"_s;
+ return compositeMetaType(name) + u".metaObject()"_s;
}
- reject(u"retrieving the metaObject of a composite type without using an instance."_s);
- return QString();
+ if (objectType->internalName() == u"QObject"_s
+ || objectType->internalName() == u"QQmlComponent"_s) {
+ return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ }
+ return metaTypeFromName(objectType) + u".metaObject()"_s;
}
QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
{
+ if (type->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type);
+ if (!name.isEmpty())
+ return compositeMetaType(name);
+ }
+
+ if (type->isListProperty() && type->valueType()->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type->valueType());
+ if (!name.isEmpty())
+ return compositeListMetaType(name);
+ }
+
return m_typeResolver->equals(m_typeResolver->genericType(type), type)
? metaTypeFromType(type)
: metaTypeFromName(type);
}
QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function,
- const InstructionAnnotations *annotations,
QQmlJS::DiagnosticMessage *error,
bool basicBlocksValidationFailed)
{
- m_annotations = annotations;
m_function = function;
m_error = error;
@@ -126,7 +157,7 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function,
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
- for (const auto &annotation : *m_annotations) {
+ for (const auto &annotation : m_annotations) {
addVariable(annotation.second.changedRegisterIndex,
annotation.second.changedRegister.resultLookupIndex(),
annotation.second.changedRegister.storedType());
@@ -210,7 +241,7 @@ QT_WARNING_POP
const QString originalValue
= u"(*static_cast<"_s + castTargetName(original.storedType())
- + u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"]))"_s;
+ + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
if (needsConversion)
result.code += conversion(original, argument, originalValue);
@@ -224,76 +255,130 @@ QT_WARNING_POP
result.code += m_body;
- for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) {
- if (argType.isValid()) {
- result.argumentTypes.append(
- m_typeResolver->originalType(argType.storedType())
- ->augmentedInternalName());
- } else {
- result.argumentTypes.append(u"void"_s);
- }
- }
- if (function->returnType) {
- result.returnType = function->returnType->internalName();
- if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- result.returnType += u'*';
+ QString signature
+ = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { unit };\n"
+ " const auto *aotContext = &c;\n"
+ " Q_UNUSED(aotContext);\n"_s;
+
+ if (function->returnType.isValid()) {
+ signature += u" argTypes[0] = %1;\n"_s.arg(
+ metaType(m_typeResolver->containedType(function->returnType)));
} else {
- result.returnType = u"void"_s;
+ signature += u" argTypes[0] = QMetaType();\n"_s;
+ }
+ result.numArguments = function->argumentTypes.length();
+ for (qsizetype i = 0; i != result.numArguments; ++i) {
+ signature += u" argTypes[%1] = %2;\n"_s.arg(
+ QString::number(i + 1),
+ metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
}
+ result.signature = signature;
return result;
}
-QString QQmlJSCodeGenerator::errorReturnValue()
+void QQmlJSCodeGenerator::generateReturnError()
{
- if (auto ret = m_function->returnType) {
- return ret->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
- ? convertStored(m_typeResolver->nullType(), ret, QString())
- : ret->internalName() + u"()"_s;
+ const auto finalizeReturn = qScopeGuard([this]() { m_body += u"return;\n"_s; });
+
+ m_body += u"aotContext->setReturnValueUndefined();\n"_s;
+ const auto ret = m_function->returnType;
+ if (!ret.isValid() || m_typeResolver->registerContains(ret, m_typeResolver->voidType()))
+ return;
+
+ m_body += u"if (argv[0]) {\n"_s;
+
+ const auto contained = m_typeResolver->containedType(ret);
+ const auto stored = ret.storedType();
+ if (contained->isReferenceType() && stored->isReferenceType()) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = nullptr;\n"_s;
+ } else if (m_typeResolver->equals(contained, stored)) {
+ m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
+ + stored->internalName() + u"();\n"_s;
+ } else {
+ m_body += u" const QMetaType returnType = "_s
+ + metaType(m_typeResolver->containedType(ret)) + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0]);\n "_s;
}
- return QString();
+
+ m_body += u"}\n"_s;
}
void QQmlJSCodeGenerator::generate_Ret()
{
INJECT_TRACE_INFO(generate_Ret);
- if (m_function->returnType) {
- const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
- if (!m_state.accumulatorVariableIn.isEmpty()) {
- const QString in = m_state.accumulatorVariableIn;
- if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->varType())) {
- m_body += u"if (!"_s + in + u".isValid())\n"_s;
- m_body += u" "_s + signalUndefined;
- } else if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
- m_body += u"if ("_s + in
- + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
- m_body += u" "_s + signalUndefined;
- } else if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
- m_body += u"if ("_s + in + u".isUndefined())\n"_s;
- m_body += u" "_s + signalUndefined;
- }
- m_body += u"return "_s
- + convertStored(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 += u"return "_s + convertStored(
- m_state.accumulatorIn().storedType(), m_function->returnType, QString());
+ const auto finalizeReturn = qScopeGuard([this]() {
+ m_body += u"return;\n"_s;
+ m_skipUntilNextLabel = true;
+ resetState();
+ });
+
+ if (!m_function->returnType.isValid())
+ return;
+
+ m_body += u"if (argv[0]) {\n"_s;
+
+ const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
+ const QString in = m_state.accumulatorVariableIn;
+
+ if (in.isEmpty()) {
+ if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
+ m_typeResolver->voidType())) {
+ m_body += signalUndefined;
+
}
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->varType())) {
+ m_body += u" if (!"_s + in + u".isValid())\n"_s;
+ m_body += u" "_s + signalUndefined;
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
+ m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
+ m_body += u" "_s + signalUndefined;
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
+ m_body += u" if ("_s + in + u".isUndefined())\n"_s;
+ m_body += u" "_s + signalUndefined;
+ }
+
+ if (m_typeResolver->registerContains(
+ m_function->returnType, m_typeResolver->voidType())) {
+ m_body += u"}\n"_s;
+ return;
+ }
+
+ const auto contained = m_typeResolver->containedType(m_function->returnType);
+ const auto stored = m_function->returnType.storedType();
+ if (m_typeResolver->equals(contained, stored)
+ || (contained->isReferenceType() && stored->isReferenceType())) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType, in)
+ + u";\n"_s;
+ } else if (m_typeResolver->registerContains(m_state.accumulatorIn(), contained)) {
+ m_body += u" const QMetaType returnType = "_s + contentType(m_state.accumulatorIn(), in)
+ + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_state.accumulatorIn(), in) + u");\n"_s;
} else {
- m_body += u"return"_s;
+ m_body += u" const auto converted = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType, in) + u";\n"_s;
+ m_body += u" const QMetaType returnType = "_s
+ + contentType(m_function->returnType, u"converted"_s)
+ + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
}
- m_body += u";\n"_s;
- m_skipUntilNextLabel = true;
- resetState();
+ m_body += u"}\n"_s;
}
void QQmlJSCodeGenerator::generate_Debug()
@@ -708,13 +793,11 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
AccumulatorConverter registers(this);
const QString baseName = registerVariable(base);
- if (!m_typeResolver->isIntegral(indexType)) {
+ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
+ voidAssignment
+ u"else "_s;
- }
-
- if (!m_typeResolver->isUnsignedInteger(indexType)) {
+ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
m_body += u"if ("_s + indexName + u" < 0)\n"_s
+ voidAssignment
+ u"else "_s;
@@ -759,9 +842,9 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
INJECT_TRACE_INFO(generate_StoreElement);
const QQmlJSRegisterContent baseType = registerType(base);
- const QQmlJSRegisterContent indexType = registerType(index);
+ const QQmlJSScope::ConstPtr indexType = m_typeResolver->containedType(registerType(index));
- if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(indexType) || !baseType.isList()) {
reject(u"StoreElement with non-list base type or non-numeric arguments"_s);
return;
}
@@ -779,26 +862,27 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
m_typeResolver->containedType(valueType)));
addInclude(u"QtQml/qjslist.h"_s);
- m_body += u"if ("_s;
- if (!m_typeResolver->isIntegral(indexType))
- m_body += u"QJSNumberCoercion::isArrayIndex("_s + indexName + u") && "_s;
- if (!m_typeResolver->isUnsignedInteger(m_typeResolver->containedType(indexType)))
- m_body += indexName + u" >= 0"_s;
+ if (!m_typeResolver->isNativeArrayIndex(indexType))
+ m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
+ else if (!m_typeResolver->isUnsignedInteger(indexType))
+ m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
+ else
+ m_body += u"{\n"_s;
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
- m_body += u" && "_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
+ m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
+ u"))\n"_s;
- m_body += u" "_s + baseName + u".replace(&"_s + baseName
+ m_body += u" "_s + baseName + u".replace(&"_s + baseName
+ u", "_s + indexName + u", "_s;
m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
+ u");\n"_s;
+ m_body += u"}\n"_s;
return;
}
if (m_state.isRegisterAffectedBySideEffects(base))
reject(u"LoadElement on a sequence potentially affected by side effects"_s);
- m_body += u") {\n"_s;
m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
+ indexName + u" + 1);\n"_s;
@@ -1209,7 +1293,7 @@ bool QQmlJSCodeGenerator::generateContentPointerCheck(
generateSetInstructionPointer();
m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
- m_body += u" return "_s + errorReturnValue() + u";\n"_s;
+ generateReturnError();
m_body += u"}\n"_s;
return needsVarContentConversion;
}
@@ -1245,7 +1329,7 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
return;
}
- if (m_typeResolver->equals(m_state.accumulatorOut().scopeType(), mathObject())) {
+ if (m_typeResolver->equals(m_state.accumulatorOut().scopeType(), m_typeResolver->mathObject())) {
QString name = m_jsUnitGenerator->lookupName(index);
double value{};
@@ -1366,7 +1450,7 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
if (stored->isListProperty()) {
m_body += m_state.accumulatorVariableOut + u" = "_s;
m_body += conversion(
- m_typeResolver->globalType(m_typeResolver->int32Type()),
+ m_typeResolver->globalType(m_typeResolver->sizeType()),
m_state.accumulatorOut(),
m_state.accumulatorVariableIn + u".count("_s + u'&'
+ m_state.accumulatorVariableIn + u')');
@@ -1374,7 +1458,7 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
} else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
|| m_typeResolver->equals(stored, m_typeResolver->stringType())) {
m_body += m_state.accumulatorVariableOut + u" = "_s
- + conversion(m_typeResolver->globalType(m_typeResolver->int32Type()),
+ + conversion(m_typeResolver->globalType(m_typeResolver->sizeType()),
m_state.accumulatorOut(),
m_state.accumulatorVariableIn + u".length()"_s)
+ u";\n"_s;
@@ -1419,7 +1503,7 @@ void QQmlJSCodeGenerator::generate_GetOptionalLookup(int index, int offset)
const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
QString accumulatorVarIn = m_state.accumulatorVariableIn;
- const auto &annotation = (*m_annotations)[currentInstructionOffset()];
+ const auto &annotation = m_annotations[currentInstructionOffset()];
if (accumulatorIn.storedType()->isReferenceType()) {
m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
generateJumpCodeWithTypeConversions(offset);
@@ -2141,10 +2225,10 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
const QQmlJSRegisterContent baseType = registerType(base);
const QString name = m_jsUnitGenerator->lookupName(index);
- if (m_typeResolver->equals(scope, mathObject())) {
+ if (m_typeResolver->equals(scope, m_typeResolver->mathObject())) {
if (inlineMathMethod(name, argc, argv))
return;
- } else if (m_typeResolver->equals(scope, consoleObject())) {
+ } else if (m_typeResolver->equals(scope, m_typeResolver->consoleObject())) {
if (inlineConsoleMethod(name, argc, argv))
return;
} else if (m_typeResolver->equals(scope, m_typeResolver->stringType())) {
@@ -2303,20 +2387,20 @@ void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
addInclude(u"QtQml/qjslist.h"_s);
const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
- + u"QLatin1String(\"Invalid array length\"));\n"_s
- + u" return "_s + errorReturnValue() + u";\n"_s;
+ + u"QLatin1String(\"Invalid array length\"));\n"_s;
const QString indexName = registerVariable(argv);
- const auto indexType = registerType(argv);
- if (!m_typeResolver->isIntegral(indexType)) {
+ const auto indexType = m_typeResolver->containedType(registerType(argv));
+ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
- + error
- + u"}\n"_s;
- } else if (!m_typeResolver->isUnsignedInteger(
- m_typeResolver->containedType(indexType))) {
+ + error;
+ generateReturnError();
+ m_body += u"}\n"_s;
+ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
m_body += u"if ("_s + indexName + u" < 0) {\n"_s
- + error
- + u"}\n"_s;
+ + error;
+ generateReturnError();
+ m_body += u"}\n"_s;
}
m_body += m_state.accumulatorVariableOut + u" = "_s
@@ -2324,7 +2408,7 @@ void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
+ u", aotContext->engine).resize("_s
+ convertStored(
- registerType(argv).storedType(), m_typeResolver->int32Type(),
+ registerType(argv).storedType(), m_typeResolver->sizeType(),
consumedRegisterVariable(argv))
+ u");\n"_s;
} else if (!m_error->isValid()) {
@@ -2379,7 +2463,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
+ conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
m_typeResolver->jsValueType()),
m_state.accumulatorVariableIn) + u");\n"_s;
- m_body += u"return "_s + errorReturnValue() + u";\n"_s;
+ generateReturnError();
m_skipUntilNextLabel = true;
resetState();
}
@@ -2857,8 +2941,7 @@ QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content
QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
- m_typeResolver->containedType(content));
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
@@ -2868,13 +2951,14 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
}
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- return metaTypeFromName(contained);
+ return metaType(contained);
- if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlSA::ScopeType::EnumScope)
- return metaTypeFromType(contained->baseType());
+ const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
+ if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return metaTypeFromType(nonComposite->baseType());
- if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
- return metaTypeFromType(m_typeResolver->listPropertyType());
+ if (stored->isListProperty() && contained->isListProperty())
+ return metaType(contained);
reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
return QString();
@@ -3223,7 +3307,7 @@ void QQmlJSCodeGenerator::generate_GetTemplateObject(int index)
QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
- m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
+ m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
const auto accumulatorIn = m_state.registers.find(Accumulator);
if (accumulatorIn != m_state.registers.end()
&& isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
@@ -3283,8 +3367,9 @@ void QQmlJSCodeGenerator::generateSetInstructionPointer()
void QQmlJSCodeGenerator::generateExceptionCheck()
{
- m_body += u"if (aotContext->engine->hasError())\n"_s;
- m_body += u" return "_s + errorReturnValue() + u";\n"_s;
+ m_body += u"if (aotContext->engine->hasError()) {\n"_s;
+ generateReturnError();
+ m_body += u"}\n"_s;
}
void QQmlJSCodeGenerator::generateEqualityOperation(
@@ -3647,8 +3732,8 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset
{
QString conversionCode;
const int absoluteOffset = nextInstructionOffset() + relativeOffset;
- const auto annotation = m_annotations->find(absoluteOffset);
- if (annotation != m_annotations->constEnd()) {
+ const auto annotation = m_annotations.find(absoluteOffset);
+ if (static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
const auto &conversions = annotation->second.typeConversions;
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
@@ -3955,6 +4040,10 @@ QString QQmlJSCodeGenerator::convertStored(
return variable + u".toDouble()"_s;
if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->int64Type())
+ || m_typeResolver->equals(to, m_typeResolver->uint64Type())) {
+ return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
+ }
if (m_typeResolver->isIntegral(to))
return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
@@ -3985,7 +4074,8 @@ QString QQmlJSCodeGenerator::convertStored(
|| m_typeResolver->equals(from, m_typeResolver->realType())
|| m_typeResolver->equals(from, m_typeResolver->stringType())) {
return u"QJSPrimitiveValue("_s + variable + u')';
- } else if (m_typeResolver->isSignedInteger(from)
+ } else if (m_typeResolver->equals(from, m_typeResolver->int16Type())
+ || m_typeResolver->equals(from, m_typeResolver->int8Type())
|| m_typeResolver->equals(from, m_typeResolver->uint16Type())
|| m_typeResolver->equals(from, m_typeResolver->uint8Type())) {
return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index f29b3dd474..2edccf31ae 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -31,13 +31,13 @@ class Q_QMLCOMPILER_EXPORT QQmlJSCodeGenerator : public QQmlJSCompilePass
{
public:
QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
- const QV4::Compiler::JSUnitGenerator *unitGenerator,
- const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger);
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations);
~QQmlJSCodeGenerator() = default;
- QQmlJSAotFunction run(const Function *function, const InstructionAnnotations *annotations,
- QQmlJS::DiagnosticMessage *error, bool basicBlocksValidationFailed);
+ QQmlJSAotFunction run(const Function *function, QQmlJS::DiagnosticMessage *error,
+ bool basicBlocksValidationFailed);
protected:
struct CodegenState : public State
@@ -242,11 +242,14 @@ protected:
const QQmlJSRegisterContent &to,
const QString &variable);
- QString errorReturnValue();
+ void generateReturnError();
void reject(const QString &thing);
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const;
QString metaTypeFromName(const QQmlJSScope::ConstPtr &type) const;
+ QString compositeMetaType(const QString &elementName) const;
+ QString compositeListMetaType(const QString &elementName) const;
+
QString contentPointer(const QQmlJSRegisterContent &content, const QString &var);
QString contentType(const QQmlJSRegisterContent &content, const QString &var);
@@ -322,18 +325,6 @@ private:
void generate_GetLookupHelper(int index);
- QQmlJSScope::ConstPtr mathObject() const
- {
- using namespace Qt::StringLiterals;
- return m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
- }
-
- QQmlJSScope::ConstPtr consoleObject() const
- {
- using namespace Qt::StringLiterals;
- return m_typeResolver->jsGlobalObject()->property(u"console"_s).type();
- }
-
QString resolveValueTypeContentPointer(
const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
const QString &variable, const QString &errorMessage);
@@ -348,7 +339,6 @@ private:
QHash<int, QString> m_labels;
const QV4::Compiler::Context *m_context = nullptr;
- const InstructionAnnotations *m_annotations = nullptr;
bool m_skipUntilNextLabel = false;
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index 7a01d20b16..a18b906d8d 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -55,6 +55,19 @@ public:
// map from register index to expected type
using VirtualRegisters = QFlatMap<int, VirtualRegister>;
+ struct BasicBlock
+ {
+ QList<int> jumpOrigins;
+ QList<int> readRegisters;
+ QList<QQmlJSScope::ConstPtr> readTypes;
+ int jumpTarget = -1;
+ bool jumpIsUnconditional = false;
+ bool isReturnBlock = false;
+ bool isThrowBlock = false;
+ };
+
+ using BasicBlocks = QFlatMap<int, BasicBlock>;
+
struct InstructionAnnotation
{
// Registers explicit read as part of the instruction.
@@ -70,13 +83,18 @@ public:
};
using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
+ struct BlocksAndAnnotations
+ {
+ BasicBlocks basicBlocks;
+ InstructionAnnotations annotations;
+ };
struct Function
{
QQmlJSScopesById addressableScopes;
QList<QQmlJSRegisterContent> argumentTypes;
QList<QQmlJSRegisterContent> registerTypes;
- QQmlJSScope::ConstPtr returnType;
+ QQmlJSRegisterContent returnType;
QQmlJSScope::ConstPtr qmlScope;
QByteArray code;
const SourceLocationTable *sourceLocations = nullptr;
@@ -86,6 +104,19 @@ public:
bool isFullyTyped = false;
};
+ struct ObjectOrArrayDefinition
+ {
+ enum {
+ ArrayClassId = -1,
+ ArrayConstruct1ArgId = -2,
+ };
+
+ int instructionOffset = -1;
+ int internalClassId = ArrayClassId;
+ int argc = 0;
+ int argv = -1;
+ };
+
struct State
{
VirtualRegisters registers;
@@ -233,10 +264,13 @@ public:
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {})
: m_jsUnitGenerator(jsUnitGenerator)
, m_typeResolver(typeResolver)
, m_logger(logger)
+ , m_basicBlocks(basicBlocks)
+ , m_annotations(annotations)
{}
protected:
@@ -245,6 +279,8 @@ protected:
QQmlJSLogger *m_logger = nullptr;
const Function *m_function = nullptr;
+ BasicBlocks m_basicBlocks;
+ InstructionAnnotations m_annotations;
QQmlJS::DiagnosticMessage *m_error = nullptr;
int firstRegisterIndex() const
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index 6514caad6f..8ecc69d1c9 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -10,6 +10,7 @@
#include <private/qqmljsimportvisitor_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsloadergenerator_p.h>
+#include <private/qqmljsoptimizations_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsshadowcheck_p.h>
#include <private/qqmljsstoragegeneralizer_p.h>
@@ -459,29 +460,10 @@ bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, Q
QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
}
-static const char *wrapCallCode = R"(
-template <typename Binding>
-void wrapCall(const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, void **argumentsPtr, Binding &&binding)
-{
- using return_type = std::invoke_result_t<Binding, const QQmlPrivate::AOTCompiledContext *, void **>;
- if constexpr (std::is_same_v<return_type, void>) {
- Q_UNUSED(dataPtr)
- binding(aotContext, argumentsPtr);
- } else {
- if (dataPtr) {
- *static_cast<return_type *>(dataPtr) = binding(aotContext, argumentsPtr);
- } else {
- binding(aotContext, argumentsPtr);
- }
- }
-}
-)";
-
static const char *funcHeaderCode = R"(
- [](const QQmlPrivate::AOTCompiledContext *context, void *data, void **argv) {
- wrapCall(context, data, argv, [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argumentsPtr) {
+ [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
Q_UNUSED(aotContext)
-Q_UNUSED(argumentsPtr)
+Q_UNUSED(argv)
)";
bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
@@ -578,13 +560,12 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
if (aotFunctions.size() <= 1) {
// FileScopeCodeIndex is always there, but it may be the only one.
writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
- "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };");
+ "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
} else {
- writeStr(wrapCallCode);
writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
- QString footer = QStringLiteral("});}\n");
+ QString footer = QStringLiteral("}\n");
for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
end = aotFunctions.constEnd();
@@ -593,25 +574,18 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
if (func.key() == FileScopeCodeIndex)
continue;
- QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
-
- QString argumentTypes = func.value().argumentTypes.join(
- QStringLiteral(">(), QMetaType::fromType<"));
- if (!argumentTypes.isEmpty()) {
- argumentTypes = QStringLiteral("QMetaType::fromType<")
- + argumentTypes + QStringLiteral(">()");
- }
+ const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
- writeStr(QStringLiteral("{ %1, QMetaType::fromType<%2>(), { %3 }, %4 },")
+ writeStr(QStringLiteral("{ %1, %2, [](QV4::ExecutableCompilationUnit *unit, "
+ "QMetaType *argTypes) {\n%3}, %4 },")
.arg(func.key())
- .arg(func.value().returnType)
- .arg(argumentTypes)
- .arg(function)
+ .arg(func->numArguments)
+ .arg(func->signature, function)
.toUtf8().constData());
}
// Conclude the list with a nullptr
- writeStr("{ 0, QMetaType::fromType<void>(), {}, nullptr }");
+ writeStr("{ 0, 0, nullptr, nullptr }");
writeStr("};\n");
}
@@ -774,32 +748,38 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
return QQmlJSAotFunction();
};
- QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger);
- auto typePropagationResult = propagator.run(function, error);
if (error->isValid())
return compileError();
- QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
- shadowCheck.run(&typePropagationResult, function, error);
+ bool basicBlocksValidationFailed = false;
+ QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
+ auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
+ auto &[blocks, annotations] = passResult;
+
+ QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = propagator.run(function, error);
if (error->isValid())
return compileError();
- bool basicBlocksValidationFailed = false;
- QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
- typePropagationResult = basicBlocks.run(function, typePropagationResult, error, m_flags, basicBlocksValidationFailed);
+ QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = shadowCheck.run(function, error);
+ if (error->isValid())
+ return compileError();
+
+ QQmlJSOptimizations optimizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
+ basicBlocks.objectAndArrayDefinitions());
+ passResult = optimizer.run(function, error);
if (error->isValid())
return compileError();
// Generalize all arguments, registers, and the return type.
- QQmlJSStorageGeneralizer generalizer(
- m_unitGenerator, &m_typeResolver, m_logger);
- typePropagationResult = generalizer.run(typePropagationResult, function, error);
+ QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = generalizer.run(function, error);
if (error->isValid())
return compileError();
- QQmlJSCodeGenerator codegen(
- context, m_unitGenerator, &m_typeResolver, m_logger);
- QQmlJSAotFunction result = codegen.run(function, &typePropagationResult, error, basicBlocksValidationFailed);
+ QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ QQmlJSAotFunction result = codegen.run(function, error, basicBlocksValidationFailed);
return error->isValid() ? compileError() : result;
}
diff --git a/src/qmlcompiler/qqmljscompiler_p.h b/src/qmlcompiler/qqmljscompiler_p.h
index aace476cf1..e358f76fef 100644
--- a/src/qmlcompiler/qqmljscompiler_p.h
+++ b/src/qmlcompiler/qqmljscompiler_p.h
@@ -48,9 +48,9 @@ struct Q_QMLCOMPILER_EXPORT QQmlJSCompileError
struct Q_QMLCOMPILER_EXPORT QQmlJSAotFunction
{
QStringList includes;
- QStringList argumentTypes;
QString code;
- QString returnType;
+ QString signature;
+ int numArguments = 0;
};
class Q_QMLCOMPILER_EXPORT QQmlJSAotCompiler
diff --git a/src/qmlcompiler/qqmljscontextualtypes_p.h b/src/qmlcompiler/qqmljscontextualtypes_p.h
index 10bbeef3b7..cccd7fd1b0 100644
--- a/src/qmlcompiler/qqmljscontextualtypes_p.h
+++ b/src/qmlcompiler/qqmljscontextualtypes_p.h
@@ -42,14 +42,23 @@ struct ContextualTypes
QQmlJSScope::ConstPtr arrayType() const { return m_arrayType; }
bool hasType(const QString &name) const { return m_types.contains(name); }
+
ImportedScope<QQmlJSScope::ConstPtr> type(const QString &name) const { return m_types[name]; }
+ QString name(const QQmlJSScope::ConstPtr &type) const { return m_names[type]; }
+
void setType(const QString &name, const ImportedScope<QQmlJSScope::ConstPtr> &type)
{
+ if (!name.startsWith(u'$'))
+ m_names.insert(type.scope, name);
m_types.insert(name, type);
}
void clearType(const QString &name)
{
- m_types[name].scope = QQmlJSScope::ConstPtr();
+ auto &scope = m_types[name].scope;
+ auto it = m_names.constFind(scope);
+ while (it != m_names.constEnd() && it.key() == scope)
+ it = m_names.erase(it);
+ scope = QQmlJSScope::ConstPtr();
}
bool isNullType(const QString &name) const
@@ -61,21 +70,37 @@ struct ContextualTypes
void addTypes(ContextualTypes &&types)
{
Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
m_types.insert(std::move(types.m_types));
}
void addTypes(const ContextualTypes &types)
{
Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
m_types.insert(types.m_types);
}
const QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> &types() const { return m_types; }
- void clearTypes() { m_types.clear(); }
+ void clearTypes()
+ {
+ m_names.clear();
+ m_types.clear();
+ }
private:
+ void insertNames(const ContextualTypes &types) {
+ for (auto it = types.m_types.constBegin(), end = types.m_types.constEnd();
+ it != end; ++it) {
+ const QString &name = it.key();
+ if (!name.startsWith(u'$'))
+ m_names.insert(it->scope, name);
+ }
+ }
+
QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> m_types;
+ QMultiHash<QQmlJSScope::ConstPtr, QString> m_names;
CompileContext m_context;
// For resolving sequence types
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 217b00c52c..09928364b1 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -116,10 +116,11 @@ void QQmlJSFunctionInitializer::populateSignature(
}
}
- if (!function->returnType) {
+ if (!function->returnType.isValid()) {
if (ast->typeAnnotation) {
- function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
- if (!function->returnType)
+ function->returnType = m_typeResolver->globalType(
+ m_typeResolver->typeFromAST(ast->typeAnnotation->type));
+ if (!function->returnType.isValid())
signatureError(u"Cannot resolve return type %1"_s.arg(
QmlIR::IRBuilder::asString(ast->typeAnnotation->type->typeId)));
}
@@ -218,9 +219,9 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
const auto property = m_objectType->property(propertyName);
if (const QQmlJSScope::ConstPtr propertyType = property.type()) {
- function.returnType = propertyType->isListProperty()
- ? m_typeResolver->qObjectListType()
- : propertyType;
+ function.returnType = m_typeResolver->globalType(propertyType->isListProperty()
+ ? m_typeResolver->qObjectListType()
+ : QQmlJSScope::ConstPtr(property.type()));
} else {
QString message = u"Cannot resolve property type %1 for binding on %2."_s
.arg(property.typeName(), propertyName);
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 9e2368b83c..bcc8e98434 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -154,6 +154,13 @@ static bool isComposite(const QQmlJSScope::ConstPtr &scope)
return scope.factory() || scope->isComposite();
}
+static QStringList aliases(const QQmlJSScope::ConstPtr &scope)
+{
+ return isComposite(scope)
+ ? QStringList()
+ : scope->aliases();
+}
+
QQmlJSImporter::QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
bool useOptionalImports)
: m_importPaths(importPaths),
@@ -407,6 +414,12 @@ void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription,
const QString modulePrefix = QStringLiteral("$module$");
QHash<QString, QList<QQmlJSScope::Export>> seenExports;
+ const auto insertAliases = [&](const QQmlJSScope::ConstPtr &scope, QTypeRevision revision) {
+ const QStringList cppAliases = aliases(scope);
+ for (const QString &alias : cppAliases)
+ types->cppNames.setType(alias, { scope, revision });
+ };
+
const auto insertExports = [&](const QQmlJSExportedScope &val, const QString &cppName) {
QQmlJSScope::Export bestExport;
@@ -479,6 +492,8 @@ void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription,
: QTypeRevision::zero();
types->cppNames.setType(cppName, { val.scope, bestRevision });
+ insertAliases(val.scope, bestRevision);
+
const QTypeRevision bestVersion = bestExport.isValid()
? bestExport.version()
: QTypeRevision::zero();
@@ -518,6 +533,7 @@ void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription,
types->qmlNames.setType(
prefixedName(internalPrefix, cppName), { val.scope, QTypeRevision() });
types->cppNames.setType(cppName, { val.scope, QTypeRevision() });
+ insertAliases(val.scope, QTypeRevision());
} else {
insertExports(val, cppName);
}
@@ -929,7 +945,7 @@ void QQmlJSImporter::setQualifiedNamesOn(const Import &import)
if (auto *factory = object.scope.factory()) {
factory->setModuleName(import.name);
} else {
- object.scope->setModuleName(import.name);
+ object.scope->setOwnModuleName(import.name);
}
}
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 25d5944be2..f048748b58 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1525,7 +1525,7 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
m_currentScope->setIsInlineComponent(true);
m_currentScope->setInlineComponentName(name);
- m_currentScope->setModuleName(m_exportedRootScope->moduleName());
+ m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
m_rootScopeImports.setType(name, { m_currentScope, revision });
m_nextIsInlineComponent = false;
}
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index f135aef8ba..b5744b0c3d 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -244,7 +244,7 @@ std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths)
}
}
#endif
-
+ Q_UNUSED(paths)
return plugins;
}
diff --git a/src/qmlcompiler/qqmljslintercodegen.cpp b/src/qmlcompiler/qqmljslintercodegen.cpp
index 2b79e34efa..175f7ee1e0 100644
--- a/src/qmlcompiler/qqmljslintercodegen.cpp
+++ b/src/qmlcompiler/qqmljslintercodegen.cpp
@@ -83,16 +83,18 @@ bool QQmlJSLinterCodegen::analyzeFunction(const QV4::Compiler::Context *context,
QQmlJS::DiagnosticMessage *error)
{
QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger,
- m_passManager);
- QQmlJSCompilePass::InstructionAnnotations annotations = propagator.run(function, error);
+ {}, {}, m_passManager);
+ auto [basicBlocks, annotations] = propagator.run(function, error);
if (!error->isValid()) {
- QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
- shadowCheck.run(&annotations, function, error);
+ QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger, basicBlocks,
+ annotations);
+ shadowCheck.run(function, error);
}
if (!error->isValid()) {
- QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger);
- generalizer.run(annotations, function, error);
+ QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger,
+ basicBlocks, annotations);
+ generalizer.run(function, error);
}
if (error->isValid()) {
diff --git a/src/qmlcompiler/qqmljsoptimizations.cpp b/src/qmlcompiler/qqmljsoptimizations.cpp
new file mode 100644
index 0000000000..1d0a7cf415
--- /dev/null
+++ b/src/qmlcompiler/qqmljsoptimizations.cpp
@@ -0,0 +1,531 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmljsoptimizations_p.h"
+#include "qqmljsbasicblocks_p.h"
+#include "qqmljsutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::Literals::StringLiterals;
+
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSOptimizations::run(const Function *function,
+ QQmlJS::DiagnosticMessage *error)
+{
+ m_function = function;
+ m_error = error;
+
+ populateBasicBlocks();
+ populateReaderLocations();
+ adjustTypes();
+
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
+}
+
+struct PendingBlock
+{
+ QQmlJSOptimizations::Conversions conversions;
+ int start = -1;
+ bool registerActive = false;
+};
+
+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 QQmlJSOptimizations::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->typeReaders.isEmpty() || reader->registerReadersAndConversions.isEmpty())) {
+
+ if (it->second.isRename) {
+ // If it's a rename, it doesn't "own" its output type. The type may
+ // still be read elsewhere, even if this register isn't. However, we're
+ // not interested in the variant or any other details of the register.
+ // Therefore just delete it.
+ it->second.changedRegisterIndex = InvalidRegister;
+ it->second.changedRegister = QQmlJSRegisterContent();
+ } else {
+ // void the output, rather than deleting it. We still need its variant.
+ bool adjusted = m_typeResolver->adjustTrackedType(
+ it->second.changedRegister.storedType(), m_typeResolver->voidType());
+ Q_ASSERT(adjusted); // Can always convert to void
+
+ adjusted = m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(it->second.changedRegister),
+ m_typeResolver->voidType());
+ Q_ASSERT(adjusted); // Can always convert to void
+ }
+ m_readerLocations.erase(reader);
+
+ // If it's not a label and has no side effects, we can drop the instruction.
+ if (!it->second.hasSideEffects) {
+ if (!it->second.readRegisters.isEmpty()) {
+ it->second.readRegisters.clear();
+ erasedReaders = true;
+ }
+ if (m_basicBlocks.find(it.key()) == m_basicBlocks.end())
+ 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()];
+ access.trackedRegister = writtenRegister;
+ 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 = QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, writeIt.key());
+ QList<PendingBlock> blocks = { { {}, blockIt->first, true } };
+ QHash<int, PendingBlock> processedBlocks;
+ bool isFirstBlock = true;
+
+ while (!blocks.isEmpty()) {
+ const PendingBlock block = blocks.takeLast();
+
+ // 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)
+ processedBlocks.insert(block.start, block);
+
+ auto nextBlock = m_basicBlocks.find(block.start);
+ auto currentBlock = nextBlock++;
+ bool registerActive = block.registerActive;
+ Conversions conversions = block.conversions;
+
+ 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) {
+ if (registerActive
+ && blockInstr->second.typeConversions.contains(writtenRegister)) {
+ conversions.insert(blockInstr.key());
+ }
+
+ for (auto readIt = blockInstr->second.readRegisters.constBegin(),
+ end = blockInstr->second.readRegisters.constEnd();
+ readIt != end; ++readIt) {
+ if (!blockInstr->second.isRename && containsAny(
+ readIt->second.content.conversionOrigins(), access.trackedTypes)) {
+ Q_ASSERT(readIt->second.content.isConversion());
+ Q_ASSERT(readIt->second.content.conversionResult());
+ access.typeReaders[blockInstr.key()]
+ = readIt->second.content.conversionResult();
+ }
+ if (registerActive && readIt->first == writtenRegister)
+ access.registerReadersAndConversions[blockInstr.key()] = conversions;
+ }
+
+ if (blockInstr->second.changedRegisterIndex == writtenRegister) {
+ conversions.clear();
+ registerActive = false;
+ }
+ }
+
+ auto scheduleBlock = [&](int blockStart) {
+ // If we find that an already processed block has the register activated by this jump,
+ // we need to re-evaluate it. We also need to propagate any newly found conversions.
+ const auto processed = processedBlocks.find(blockStart);
+ if (processed == processedBlocks.end()) {
+ blocks.append({conversions, blockStart, registerActive});
+ } else if (registerActive && !processed->registerActive) {
+ blocks.append({conversions, blockStart, registerActive});
+ } else {
+
+ // TODO: Use unite() once it is fixed.
+ // We don't use unite() here since it would be more expensive. unite()
+ // effectively loops on only insert() and insert() does a number of checks
+ // each time. We trade those checks for calculating the hash twice on each
+ // iteration. Calculating the hash is very cheap for integers.
+ Conversions merged = processed->conversions;
+ for (const int conversion : std::as_const(conversions)) {
+ if (!merged.contains(conversion))
+ merged.insert(conversion);
+ }
+
+ if (merged.size() > processed->conversions.size())
+ blocks.append({std::move(merged), blockStart, registerActive});
+ }
+ };
+
+ if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
+ scheduleBlock(nextBlock->first);
+
+ const int jumpTarget = currentBlock->second.jumpTarget;
+ if (jumpTarget != -1)
+ scheduleBlock(jumpTarget);
+
+ if (isFirstBlock)
+ isFirstBlock = false;
+ }
+
+ 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 typeIt = readers->typeReaders.begin();
+ typeIt != readers->typeReaders.end();) {
+ if (m_annotations.contains(typeIt.key()))
+ ++typeIt;
+ else
+ typeIt = readers->typeReaders.erase(typeIt);
+ }
+
+ for (auto registerIt = readers->registerReadersAndConversions.begin();
+ registerIt != readers->registerReadersAndConversions.end();) {
+ if (m_annotations.contains(registerIt.key()))
+ ++registerIt;
+ else
+ registerIt = readers->registerReadersAndConversions.erase(registerIt);
+ }
+ }
+
+ if (!eraseDeadStore(it))
+ newAnnotations.appendOrdered(it);
+ }
+
+ m_annotations = newAnnotations.take();
+ }
+}
+
+bool QQmlJSOptimizations::canMove(int instructionOffset,
+ const QQmlJSOptimizations::RegisterAccess &access) const
+{
+ if (access.registerReadersAndConversions.size() != 1)
+ return false;
+ return QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, instructionOffset)
+ == QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, access.registerReadersAndConversions.begin().key());
+}
+
+QList<QQmlJSCompilePass::ObjectOrArrayDefinition>
+QQmlJSBasicBlocks::objectAndArrayDefinitions() const
+{
+ return m_objectAndArrayDefinitions;
+}
+
+static QString adjustErrorMessage(
+ const QQmlJSScope::ConstPtr &origin, const QQmlJSScope::ConstPtr &conversion) {
+ return QLatin1String("Cannot convert from ")
+ + origin->internalName() + QLatin1String(" to ") + conversion->internalName();
+}
+
+static QString adjustErrorMessage(
+ const QQmlJSScope::ConstPtr &origin, const QList<QQmlJSScope::ConstPtr> &conversions) {
+ if (conversions.size() == 1)
+ return adjustErrorMessage(origin, conversions[0]);
+
+ QString types;
+ for (const QQmlJSScope::ConstPtr &type : conversions) {
+ if (!types.isEmpty())
+ types += QLatin1String(", ");
+ types += type->internalName();
+ }
+ return QLatin1String("Cannot convert from ")
+ + origin->internalName() + QLatin1String(" to union of ") + types;
+}
+
+void QQmlJSOptimizations::adjustTypes()
+{
+ using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>;
+
+ QHash<int, QList<int>> liveConversions;
+ QHash<int, QList<int>> movableReads;
+
+ const auto handleRegisterReadersAndConversions
+ = [&](QHash<int, RegisterAccess>::const_iterator it) {
+ for (auto conversions = it->registerReadersAndConversions.constBegin(),
+ end = it->registerReadersAndConversions.constEnd(); conversions != end;
+ ++conversions) {
+ if (conversions->isEmpty() && canMove(it.key(), it.value()))
+ movableReads[conversions.key()].append(it->trackedRegister);
+ for (int conversion : *conversions)
+ liveConversions[conversion].append(it->trackedRegister);
+ }
+ };
+
+ const auto transformRegister = [&](const QQmlJSRegisterContent &content) {
+ const QQmlJSScope::ConstPtr conversion
+ = m_typeResolver->storedType(m_typeResolver->containedType(content));
+ if (!m_typeResolver->adjustTrackedType(content.storedType(), conversion))
+ setError(adjustErrorMessage(content.storedType(), conversion));
+ };
+
+ // Handle the array definitions first.
+ // Changing the array type changes the expected element types.
+ auto adjustArray = [&](int instructionOffset, int mode) {
+ auto it = m_readerLocations.find(instructionOffset);
+ if (it == m_readerLocations.end())
+ return;
+
+ const InstructionAnnotation &annotation = m_annotations[instructionOffset];
+ if (annotation.readRegisters.isEmpty())
+ return;
+
+ Q_ASSERT(it->trackedTypes.size() == 1);
+ Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
+
+ if (it->trackedTypes[0]->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
+ return; // Constructed something else.
+
+ if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
+ setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
+
+ // Now we don't adjust the type we store, but rather the type we expect to read. We
+ // can do this because we've tracked the read type when we defined the array in
+ // QQmlJSTypePropagator.
+ if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
+ const QQmlJSRegisterContent content = annotation.readRegisters.begin().value().content;
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
+
+ // If it's the 1-arg Array ctor, and the argument is a number, that's special.
+ if (mode != ObjectOrArrayDefinition::ArrayConstruct1ArgId
+ || !m_typeResolver->equals(contained, m_typeResolver->realType())) {
+ if (!m_typeResolver->adjustTrackedType(contained, valueType))
+ setError(adjustErrorMessage(contained, valueType));
+
+ // We still need to adjust the stored type, too.
+ transformRegister(content);
+ }
+ }
+
+ handleRegisterReadersAndConversions(it);
+ m_readerLocations.erase(it);
+ };
+
+ // Handle the object definitions.
+ // Changing the object type changes the expected property types.
+ const auto adjustObject = [&](const ObjectOrArrayDefinition &object) {
+ auto it = m_readerLocations.find(object.instructionOffset);
+ if (it == m_readerLocations.end())
+ return;
+
+ const InstructionAnnotation &annotation = m_annotations[object.instructionOffset];
+
+ Q_ASSERT(it->trackedTypes.size() == 1);
+ QQmlJSScope::ConstPtr resultType = it->trackedTypes[0];
+
+ Q_ASSERT(resultType == m_typeResolver->containedType(annotation.changedRegister));
+ Q_ASSERT(!annotation.readRegisters.isEmpty());
+
+ if (!m_typeResolver->adjustTrackedType(resultType, it->typeReaders.values()))
+ setError(adjustErrorMessage(resultType, it->typeReaders.values()));
+
+ if (m_typeResolver->equals(resultType, m_typeResolver->varType())
+ || m_typeResolver->equals(resultType, m_typeResolver->variantMapType())) {
+ // It's all variant anyway
+ return;
+ }
+
+ const int classSize = m_jsUnitGenerator->jsClassSize(object.internalClassId);
+ Q_ASSERT(object.argc >= classSize);
+
+ for (int i = 0; i < classSize; ++i) {
+ // Now we don't adjust the type we store, but rather the types we expect to read. We
+ // can do this because we've tracked the read types when we defined the object in
+ // QQmlJSTypePropagator.
+
+ const QString propName = m_jsUnitGenerator->jsClassMember(object.internalClassId, i);
+ const QQmlJSMetaProperty property = resultType->property(propName);
+ if (!property.isValid()) {
+ setError(resultType->internalName() + QLatin1String(" has no property called ")
+ + propName);
+ continue;
+ }
+ const QQmlJSScope::ConstPtr propType = property.type();
+ if (propType.isNull()) {
+ setError(QLatin1String("Cannot resolve type of property ") + propName);
+ continue;
+ }
+ const QQmlJSRegisterContent content = annotation.readRegisters[object.argv + i].content;
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
+ if (!m_typeResolver->adjustTrackedType(contained, propType))
+ setError(adjustErrorMessage(contained, propType));
+
+ // We still need to adjust the stored type, too.
+ transformRegister(content);
+ }
+
+ // The others cannot be adjusted. We don't know their names, yet.
+ // But we might still be able to use the variants.
+ };
+
+ // Iterate in reverse so that we can have nested lists and objects and the types are propagated
+ // from the outer lists/objects to the inner ones.
+ for (auto it = m_objectAndArrayDefinitions.crbegin(), end = m_objectAndArrayDefinitions.crend();
+ it != end; ++it) {
+ switch (it->internalClassId) {
+ case ObjectOrArrayDefinition::ArrayClassId:
+ case ObjectOrArrayDefinition::ArrayConstruct1ArgId:
+ adjustArray(it->instructionOffset, it->internalClassId);
+ break;
+ default:
+ adjustObject(*it);
+ break;
+ }
+ }
+
+ for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
+ handleRegisterReadersAndConversions(it);
+
+ // There is always one first occurrence of any tracked type. Conversions don't change
+ // the type.
+ if (it->trackedTypes.size() != 1)
+ continue;
+
+ // Don't adjust renamed values. We only adjust the originals.
+ const int writeLocation = it.key();
+ if (writeLocation >= 0 && m_annotations[writeLocation].isRename)
+ continue;
+
+ if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
+ setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
+ }
+
+
+ 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 conversion = i->second.typeConversions.begin(),
+ conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd;
+ ++conversion) {
+ if (!liveConversions[i.key()].contains(conversion.key()))
+ continue;
+
+ QQmlJSScope::ConstPtr newResult;
+ const auto content = conversion->second.content;
+ if (content.isConversion()) {
+ QQmlJSScope::ConstPtr conversionResult = content.conversionResult();
+ const auto conversionOrigins = content.conversionOrigins();
+ for (const auto &origin : conversionOrigins)
+ newResult = m_typeResolver->merge(newResult, origin);
+ if (!m_typeResolver->adjustTrackedType(conversionResult, newResult))
+ setError(adjustErrorMessage(conversionResult, newResult));
+ }
+ transformRegister(content);
+ newRegisters.appendOrdered(conversion);
+ }
+ i->second.typeConversions = newRegisters.take();
+
+ for (int movable : std::as_const(movableReads[i.key()]))
+ i->second.readRegisters[movable].canMove = true;
+ }
+}
+
+void QQmlJSOptimizations::populateBasicBlocks()
+{
+ for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
+ blockNext != blockEnd;) {
+
+ const auto blockIt = blockNext++;
+ BasicBlock &block = blockIt->second;
+ QList<QQmlJSScope::ConstPtr> writtenTypes;
+ QList<int> 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) {
+ if (!instruction.isRename) {
+ Q_ASSERT(it->second.content.isConversion());
+ for (const QQmlJSScope::ConstPtr &origin :
+ it->second.content.conversionOrigins()) {
+ if (!writtenTypes.contains(origin))
+ block.readTypes.append(origin);
+ }
+ }
+ if (!writtenRegisters.contains(it->first))
+ block.readRegisters.append(it->first);
+ }
+
+ // If it's just a renaming, the type has existed in a different register before.
+ if (instruction.changedRegisterIndex != InvalidRegister) {
+ if (!instruction.isRename) {
+ writtenTypes.append(m_typeResolver->trackedContainedType(
+ instruction.changedRegister));
+ }
+ writtenRegisters.append(instruction.changedRegisterIndex);
+ }
+ }
+
+ QQmlJSUtils::deduplicate(block.readTypes);
+ QQmlJSUtils::deduplicate(block.readRegisters);
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsoptimizations_p.h b/src/qmlcompiler/qqmljsoptimizations_p.h
new file mode 100644
index 0000000000..0d7091ab49
--- /dev/null
+++ b/src/qmlcompiler/qqmljsoptimizations_p.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSOPTIMIZATIONS_P_H
+#define QQMLJSOPTIMIZATIONS_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>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLCOMPILER_EXPORT QQmlJSOptimizations : public QQmlJSCompilePass
+{
+public:
+ using Conversions = QSet<int>;
+
+ QQmlJSOptimizations(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations,
+ QList<ObjectOrArrayDefinition> objectAndArrayDefinitions)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
+ m_objectAndArrayDefinitions{ objectAndArrayDefinitions }
+ {
+ }
+
+ ~QQmlJSOptimizations() = default;
+
+ BlocksAndAnnotations run(const Function *function, QQmlJS::DiagnosticMessage *error);
+
+private:
+ struct RegisterAccess
+ {
+ QList<QQmlJSScope::ConstPtr> trackedTypes;
+ QHash<int, QQmlJSScope::ConstPtr> typeReaders;
+ QHash<int, Conversions> registerReadersAndConversions;
+ int trackedRegister;
+ };
+
+ QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override
+ {
+ return ProcessInstruction;
+ }
+ void endInstruction(QV4::Moth::Instr::Type) override { }
+
+ void populateBasicBlocks();
+ void populateReaderLocations();
+ void adjustTypes();
+ bool canMove(int instructionOffset, const RegisterAccess &access) const;
+
+ QHash<int, RegisterAccess> m_readerLocations;
+ QList<ObjectOrArrayDefinition> m_objectAndArrayDefinitions;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSOPTIMIZATIONS_P_H
diff --git a/src/qmlcompiler/qqmljsregistercontent.cpp b/src/qmlcompiler/qqmljsregistercontent.cpp
index fa89dbe0bc..2c5d562e5b 100644
--- a/src/qmlcompiler/qqmljsregistercontent.cpp
+++ b/src/qmlcompiler/qqmljsregistercontent.cpp
@@ -13,7 +13,6 @@ QString QQmlJSRegisterContent::descriptiveName() const
if (m_storedType.isNull())
return u"(invalid type)"_s;
- QString result = m_storedType->internalName() + u" of "_s;
const auto scope = [this]() -> QString {
if (m_scope.isNull())
return u"(invalid type)::"_s;
@@ -25,36 +24,46 @@ QString QQmlJSRegisterContent::descriptiveName() const
+ u"::"_s;
};
+ QString result;
switch (m_content.index()) {
- case Type:
- return result
- + std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).first->internalName();
+ case Type: {
+ auto contained = std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).first;
+ result += contained->internalName();
+ if (m_storedType->internalName() != contained->internalName())
+ result += u" stored as "_s + m_storedType->internalName();
+ return result;
+ }
case Property: {
const QQmlJSMetaProperty prop = std::get<PropertyLookup>(m_content).property;
- return result + scope() + prop.propertyName() + u" with type "_s + prop.typeName();
+ result += scope() + prop.propertyName() + u" with type "_s + prop.typeName();
+ if (m_storedType->internalName() != prop.typeName())
+ result += u" (stored as "_s + m_storedType->internalName() + u")";
+ return result;
}
case Method: {
const auto methods = std::get<QList<QQmlJSMetaMethod>>(m_content);
if (methods.isEmpty())
- return result + scope() + u"(unknown method)"_s;
+ result = scope() + u"(unknown method)"_s;
else
- return result + scope() + methods[0].methodName() + u"(...)"_s;
+ result = scope() + methods[0].methodName() + u"(...)"_s;
+ return result + u" (stored as "_s + m_storedType->internalName() + u")";
}
case Enum: {
const auto e = std::get<std::pair<QQmlJSMetaEnum, QString>>(m_content);
if (e.second.isEmpty())
- return result + scope() + e.first.name();
+ result = scope() + e.first.name();
else
- return result + scope() + e.first.name() + u"::"_s + e.second;
+ result = scope() + e.first.name() + u"::"_s + e.second;
+ return result + u" (stored as "_s + m_storedType->internalName() + u")";
}
case ImportNamespace: {
return u"import namespace %1"_s.arg(std::get<uint>(m_content));
}
case Conversion: {
- return u"conversion to %1"_s.arg(
- std::get<ConvertedTypes>(m_content).result->internalName());
+ return u"conversion to %1"_s.arg(std::get<ConvertedTypes>(m_content).result->internalName());
}
}
+
Q_UNREACHABLE_RETURN(result + u"wat?"_s);
}
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index a1bc295838..b32f86226e 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -1011,6 +1011,22 @@ void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
m_baseTypeNameOrError = baseTypeError;
}
+/*!
+\internal
+The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
+the QmlComponent or the root is reached to find out the module name of the component in which `this`
+resides.
+*/
+QString QQmlJSScope::moduleName() const
+{
+ for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
+ const QString name = it->ownModuleName();
+ if (!name.isEmpty())
+ return name;
+ }
+ return {};
+}
+
QString QQmlJSScope::baseTypeError() const
{
return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
@@ -1129,7 +1145,7 @@ bool QQmlJSScope::Export::isValid() const
void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
{
- scope->setModuleName(m_moduleName);
+ scope->setOwnModuleName(m_moduleName);
QQmlJSTypeReader typeReader(m_importer, m_filePath);
typeReader(scope);
m_importer->m_globalWarnings.append(typeReader.errors());
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 5c3ca9001c..97ec6cc004 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -261,6 +261,9 @@ public:
bool isComponentRootElement() const;
+ void setAliases(const QStringList &aliases) { m_aliases = aliases; }
+ QStringList aliases() const { return m_aliases; }
+
void setInterfaceNames(const QStringList& interfaces) { m_interfaceNames = interfaces; }
QStringList interfaceNames() const { return m_interfaceNames; }
@@ -282,8 +285,9 @@ public:
QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; }
QTypeRevision baseTypeRevision() const { return m_baseType.revision; }
- QString moduleName() const { return m_moduleName; }
- void setModuleName(const QString &moduleName) { m_moduleName = moduleName; }
+ QString moduleName() const;
+ QString ownModuleName() const { return m_moduleName; }
+ void setOwnModuleName(const QString &moduleName) { m_moduleName = moduleName; }
void clearBaseType() { m_baseType = {}; }
void setBaseTypeError(const QString &baseTypeError);
@@ -515,6 +519,7 @@ private:
ImportedScope<QQmlJSScope::WeakConstPtr> m_baseType;
ScopeType m_scopeType = ScopeType::QMLScope;
+ QStringList m_aliases;
QStringList m_interfaceNames;
QStringList m_ownDeferredNames;
QStringList m_ownImmediateNames;
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index ca3c0c6c68..d542767dce 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -34,11 +34,9 @@ using namespace Qt::StringLiterals;
* arguments and return types into "var".
*/
-void QQmlJSShadowCheck::run(
- InstructionAnnotations *annotations, const Function *function,
- QQmlJS::DiagnosticMessage *error)
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSShadowCheck::run(const Function *function,
+ QQmlJS::DiagnosticMessage *error)
{
- m_annotations = annotations;
m_function = function;
m_error = error;
m_state = initialState(function);
@@ -52,6 +50,8 @@ void QQmlJSShadowCheck::run(
if (checkBaseType(base) == Shadowable)
break;
}
+
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
@@ -89,7 +89,7 @@ void QQmlJSShadowCheck::handleStore(int base, const QString &memberName)
{
const int instructionOffset = currentInstructionOffset();
const QQmlJSRegisterContent &readAccumulator
- = (*m_annotations)[instructionOffset].readRegisters[Accumulator].content;
+ = m_annotations[instructionOffset].readRegisters[Accumulator].content;
const auto baseType = m_state.registers[base].content;
// If the accumulator is already read as var, we don't have to do anything.
@@ -136,7 +136,7 @@ void QQmlJSShadowCheck::generate_CallPropertyLookup(int nameIndex, int base, int
QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type)
{
- m_state = nextStateFromAnnotations(m_state, *m_annotations);
+ m_state = nextStateFromAnnotations(m_state, m_annotations);
return (m_state.hasSideEffects() || m_state.changedRegisterIndex() != InvalidRegister)
? ProcessInstruction
: SkipInstruction;
@@ -189,7 +189,7 @@ QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
// Make it "var". We don't know what it is.
const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
- InstructionAnnotation &currentAnnotation = (*m_annotations)[currentInstructionOffset()];
+ InstructionAnnotation &currentAnnotation = m_annotations[currentInstructionOffset()];
if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
m_typeResolver->adjustOriginalType(
@@ -230,7 +230,7 @@ void QQmlJSShadowCheck::checkResettable(
const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
QQmlJSRegisterContent &readAccumulator
- = (*m_annotations)[instructionOffset].readRegisters[Accumulator].content;
+ = m_annotations[instructionOffset].readRegisters[Accumulator].content;
readAccumulator = m_typeResolver->convert(readAccumulator, varContent);
}
diff --git a/src/qmlcompiler/qqmljsshadowcheck_p.h b/src/qmlcompiler/qqmljsshadowcheck_p.h
index 4287d3e2e6..dfa00134cb 100644
--- a/src/qmlcompiler/qqmljsshadowcheck_p.h
+++ b/src/qmlcompiler/qqmljsshadowcheck_p.h
@@ -22,14 +22,14 @@ class Q_QMLCOMPILER_EXPORT QQmlJSShadowCheck : public QQmlJSCompilePass
{
public:
QQmlJSShadowCheck(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations)
+ : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger, basicBlocks, annotations)
{}
~QQmlJSShadowCheck() = default;
- void run(InstructionAnnotations *annotations, const Function *function,
- QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(const Function *function, QQmlJS::DiagnosticMessage *error);
private:
struct ResettableStore {
@@ -62,7 +62,6 @@ private:
QList<QQmlJSRegisterContent> m_baseTypes;
QSet<QQmlJSRegisterContent> m_adjustedTypes;
- InstructionAnnotations *m_annotations = nullptr;
State m_state;
};
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 543dec9ff6..937c35ddcd 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -19,20 +19,19 @@ QT_BEGIN_NAMESPACE
* operates only on the annotations and the function description.
*/
-QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
- InstructionAnnotations annotations, Function *function,
- QQmlJS::DiagnosticMessage *error)
+QQmlJSCompilePass::BlocksAndAnnotations
+QQmlJSStorageGeneralizer::run(Function *function, QQmlJS::DiagnosticMessage *error)
{
m_error = error;
- if (QQmlJSScope::ConstPtr &returnType = function->returnType) {
+ if (QQmlJSRegisterContent &returnType = function->returnType; returnType.isValid()) {
if (QQmlJSScope::ConstPtr stored = m_typeResolver->genericType(
- returnType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- returnType = stored;
+ returnType.storedType(), QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
+ returnType = returnType.storedIn(stored);
} else {
setError(QStringLiteral("Cannot store the return type %1.")
- .arg(returnType->internalName(), 0));
- return InstructionAnnotations();
+ .arg(returnType.storedType()->internalName()));
+ return {};
}
}
@@ -53,12 +52,12 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
transformRegister(argument);
}
- for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
transformRegister(i->second.changedRegister);
transformRegisters(i->second.typeConversions);
}
- return annotations;
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
index 0ea6977bba..9ef4699fca 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
@@ -22,12 +22,12 @@ class Q_QMLCOMPILER_EXPORT QQmlJSStorageGeneralizer : public QQmlJSCompilePass
{
public:
QQmlJSStorageGeneralizer(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations)
+ : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger, basicBlocks, annotations)
{}
- InstructionAnnotations run(InstructionAnnotations annotations, Function *function,
- QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(Function *function, QQmlJS::DiagnosticMessage *error);
protected:
// We don't have to use the byte code here. We only transform the instruction annotations.
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 34e46d4256..bebc01a8ef 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -201,6 +201,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setOwnParentPropertyName(readStringBinding(script));
} else if (name == QLatin1String("exports")) {
exports = readExports(script);
+ } else if (name == QLatin1String("aliases")) {
+ readAliases(script, scope);
} else if (name == QLatin1String("interfaces")) {
readInterfaces(script, scope);
} else if (name == QLatin1String("exportMetaObjectRevisions")) {
@@ -247,7 +249,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
addWarning(script->firstSourceLocation(),
tr("Expected only name, prototype, defaultProperty, attachedType, "
"valueType, exports, interfaces, isSingleton, isCreatable, "
- "isStructured, isComposite, hasCustomParser, "
+ "isStructured, isComposite, hasCustomParser, aliases, "
"exportMetaObjectRevisions, deferredNames, and immediateNames "
"in script bindings, not \"%1\".")
.arg(name));
@@ -686,6 +688,12 @@ QList<QQmlJSScope::Export> QQmlJSTypeDescriptionReader::readExports(UiScriptBind
return exports;
}
+void QQmlJSTypeDescriptionReader::readAliases(
+ QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
+{
+ scope->setAliases(readStringList(ast));
+}
+
void QQmlJSTypeDescriptionReader::readInterfaces(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
{
auto *arrayLit = getArray(ast);
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader_p.h b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
index 914ca43409..2bbae61fd6 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader_p.h
+++ b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
@@ -55,6 +55,7 @@ private:
QTypeRevision readNumericVersionBinding(QQmlJS::AST::UiScriptBinding *ast);
int readIntBinding(QQmlJS::AST::UiScriptBinding *ast);
QList<QQmlJSScope::Export> readExports(QQmlJS::AST::UiScriptBinding *ast);
+ void readAliases(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope);
void readInterfaces(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope);
void checkMetaObjectRevisions(
QQmlJS::AST::UiScriptBinding *ast, QList<QQmlJSScope::Export> *exports);
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 69df1ebcb3..d7a7d68d9f 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -28,18 +28,20 @@ using namespace Qt::StringLiterals;
QQmlJSTypePropagator::QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger, QQmlSA::PassManager *passManager)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger),
+ QQmlJSLogger *logger, BasicBlocks basicBlocks,
+ InstructionAnnotations annotations,
+ QQmlSA::PassManager *passManager)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
m_passManager(passManager)
{
}
-QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSTypePropagator::run(
const Function *function, QQmlJS::DiagnosticMessage *error)
{
m_function = function;
m_error = error;
- m_returnType = m_typeResolver->globalType(m_function->returnType);
+ m_returnType = m_function->returnType;
do {
// Reset the error if we need to do another pass
@@ -48,6 +50,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
+ m_state.annotations = m_annotations;
m_state.State::operator=(initialState(m_function));
reset();
@@ -58,7 +61,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
// This means that we won't start over for the same reason again.
} while (m_state.needsMorePasses);
- return m_state.annotations;
+ return { std::move(m_basicBlocks), std::move(m_state.annotations) };
}
#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \
@@ -70,16 +73,21 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
qmlCompiler, QQmlJS::SourceLocation()); \
return;
+void QQmlJSTypePropagator::generate_ret_SAcheck()
+{
+ if (!m_function->isProperty)
+ return;
+ QQmlSA::PassManagerPrivate::get(m_passManager)
+ ->analyzeBinding(QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
void QQmlJSTypePropagator::generate_Ret()
{
- if (m_passManager != nullptr && m_function->isProperty) {
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeBinding(
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
- QQmlJSScope::createQQmlSAElement(
- m_typeResolver->containedType(m_state.accumulatorIn())),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ generate_ret_SAcheck();
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
@@ -87,8 +95,8 @@ void QQmlJSTypePropagator::generate_Ret()
m_state.accumulatorIn(), m_typeResolver->voidType())) {
// You can always return undefined.
} else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()) {
- setError(u"function without return type annotation returns %1"_s
- .arg(m_state.accumulatorIn().descriptiveName()));
+ setError(u"function without return type annotation returns %1. This may prevent proper "_s
+ u"compilation to Cpp."_s.arg(m_state.accumulatorIn().descriptiveName()));
if (m_function->isFullyTyped) {
// Do not complain if the function didn't have a valid annotation in the first place.
@@ -540,6 +548,17 @@ bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const
return true;
}
+
+void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup_SAcheck(const QString &name)
+{
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
// LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
@@ -582,18 +601,23 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
return;
}
- if (m_passManager != nullptr) {
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ generate_LoadQmlContextPropertyLookup_SAcheck(name);
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ScopeAttached)
m_attachedContext = QQmlJSScope::ConstPtr();
}
+void QQmlJSTypePropagator::generate_StoreNameCommon_SAcheck(const QQmlJSRegisterContent &in, const QString &name)
+{
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
+ QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(in)),
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
/*!
\internal
As far as type propagation is involved, StoreNameSloppy and
@@ -642,14 +666,8 @@ void QQmlJSTypePropagator::generate_StoreNameCommon(int nameIndex)
.arg(in.descriptiveName(), type.descriptiveName()));
}
- if (m_passManager != nullptr) {
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
- QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(in)),
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ generate_StoreNameCommon_SAcheck(in, name);
if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
@@ -724,7 +742,7 @@ void QQmlJSTypePropagator::generate_LoadElement(int base)
if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
const auto contained = m_typeResolver->containedType(m_state.accumulatorIn());
if (m_typeResolver->isSignedInteger(contained))
- addReadAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type()));
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->sizeType()));
else if (m_typeResolver->isUnsignedInteger(contained))
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type()));
else
@@ -780,6 +798,21 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
m_state.setHasSideEffects(true);
}
+void QQmlJSTypePropagator::propagatePropertyLookup_SAcheck(const QString &propertyName)
+{
+ const bool isAttached =
+ m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached;
+
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ propertyName,
+ QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
+ : m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName, int lookupIndex)
{
setAccumulator(
@@ -912,19 +945,8 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName,
}
}
- if (m_passManager != nullptr) {
- const bool isAttached =
- m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached;
-
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
- QQmlJSScope::createQQmlSAElement(
- m_typeResolver->containedType(m_state.accumulatorIn())),
- propertyName,
- QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
- : m_function->qmlScope),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ propagatePropertyLookup_SAcheck(propertyName);
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached)
m_attachedContext = m_typeResolver->containedType(m_state.accumulatorIn());
@@ -968,6 +990,21 @@ void QQmlJSTypePropagator::generate_GetOptionalLookup(int index, int offset)
propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
}
+void QQmlJSTypePropagator::generate_StoreProperty_SAcheck(const QString propertyName, const QQmlJSRegisterContent &callBase)
+{
+ const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
+
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
+ QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(callBase)),
+ propertyName,
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
+ : m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
{
auto callBase = m_state.registers[base].content;
@@ -1001,19 +1038,8 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
return;
}
- if (m_passManager != nullptr) {
- const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
-
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
- QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(callBase)),
- propertyName,
- QQmlJSScope::createQQmlSAElement(
- m_typeResolver->containedType(m_state.accumulatorIn())),
- QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
- : m_function->qmlScope),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ generate_StoreProperty_SAcheck(propertyName, callBase);
// If the input can hold undefined we must not coerce it to the property type
// as that might eliminate an undefined value. For example, undefined -> string
@@ -1090,77 +1116,93 @@ static bool isLoggingMethod(const QString &consoleMethod)
|| consoleMethod == u"warn" || consoleMethod == u"error";
}
-void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
+void QQmlJSTypePropagator::generate_CallProperty_SCMath(int base, int argc, int argv)
{
- Q_ASSERT(m_state.registers.contains(base));
- const auto callBase = m_state.registers[base].content;
- const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
+ // 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.
- const QQmlJSScope::ConstPtr mathObject
- = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
- if (m_typeResolver->registerContains(callBase, mathObject)) {
+ addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
- // 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->returnType(
+ m_typeResolver->realType(), QQmlJSRegisterContent::MethodReturnValue,
+ m_typeResolver->mathObject());
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, realType);
+ setAccumulator(realType);
+}
- QQmlJSRegisterContent realType = m_typeResolver->returnType(
- m_typeResolver->realType(), QQmlJSRegisterContent::MethodReturnValue, mathObject);
- for (int i = 0; i < argc; ++i)
- addReadRegister(argv + i, realType);
- setAccumulator(realType);
- return;
- }
+void QQmlJSTypePropagator::generate_CallProperty_SCconsole(int base, int argc, int argv)
+{
+ const QQmlJSRegisterContent voidType
+ = m_typeResolver->globalType(m_typeResolver->voidType());
- const QQmlJSScope::ConstPtr consoleType
- = m_typeResolver->jsGlobalObject()->property(u"console"_s).type();
- if (m_typeResolver->registerContains(callBase, consoleType) && isLoggingMethod(propertyName)) {
+ // If we call a method on the console object we don't need the console object.
+ addReadRegister(base, voidType);
- const QQmlJSRegisterContent voidType
- = m_typeResolver->globalType(m_typeResolver->voidType());
+ const QQmlJSRegisterContent stringType
+ = m_typeResolver->globalType(m_typeResolver->stringType());
- // If we call a method on the console object we don't need the console object.
- addReadRegister(base, voidType);
+ if (argc > 0) {
+ const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
+ const QQmlJSScope::ConstPtr firstArg = m_typeResolver->containedType(firstContent);
+ switch (firstArg->accessSemantics()) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ // We cannot know whether this will be a logging category at run time.
+ // Therefore we always pass any object types as special last argument.
+ addReadRegister(argv, m_typeResolver->globalType(
+ m_typeResolver->genericType(firstArg)));
+ break;
+ case QQmlJSScope::AccessSemantics::Sequence:
+ addReadRegister(argv, firstContent);
+ break;
+ default:
+ addReadRegister(argv, stringType);
+ break;
+ }
+ }
- const QQmlJSRegisterContent stringType
- = m_typeResolver->globalType(m_typeResolver->stringType());
+ for (int i = 1; i < argc; ++i) {
+ const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
+ const QQmlJSScope::ConstPtr arg = m_typeResolver->containedType(argContent);
+ addReadRegister(
+ argv + i,
+ arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ ? argContent
+ : stringType);
+ }
- if (argc > 0) {
- const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
- const QQmlJSScope::ConstPtr firstArg = m_typeResolver->containedType(firstContent);
- switch (firstArg->accessSemantics()) {
- case QQmlJSScope::AccessSemantics::Reference:
- // We cannot know whether this will be a logging category at run time.
- // Therefore we always pass any object types as special last argument.
- addReadRegister(argv, m_typeResolver->globalType(
- m_typeResolver->genericType(firstArg)));
- break;
- case QQmlJSScope::AccessSemantics::Sequence:
- addReadRegister(argv, firstContent);
- break;
- default:
- addReadRegister(argv, stringType);
- break;
- }
- }
+ m_state.setHasSideEffects(true);
+ setAccumulator(m_typeResolver->returnType(
+ m_typeResolver->voidType(), QQmlJSRegisterContent::MethodReturnValue,
+ m_typeResolver->consoleObject()));
+}
- for (int i = 1; i < argc; ++i) {
- const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
- const QQmlJSScope::ConstPtr arg = m_typeResolver->containedType(argContent);
- addReadRegister(
- argv + i,
- arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
- ? argContent
- : stringType);
- }
+void QQmlJSTypePropagator::generate_callProperty_SAcheck(const QString propertyName, const QQmlJSScope::ConstPtr &baseType)
+{
+ // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(baseType), propertyName,
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
- m_state.setHasSideEffects(true);
- setAccumulator(m_typeResolver->returnType(
- m_typeResolver->voidType(), QQmlJSRegisterContent::MethodReturnValue, consoleType));
+void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
+{
+ Q_ASSERT(m_state.registers.contains(base));
+ const auto callBase = m_state.registers[base].content;
+ const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->mathObject())) {
+ generate_CallProperty_SCMath(base, argc, argv);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
+ generate_CallProperty_SCconsole(base, argc, argv);
return;
}
@@ -1206,14 +1248,8 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
checkDeprecated(baseType, propertyName, true);
- if (m_passManager != nullptr) {
- // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
- QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
- QQmlJSScope::createQQmlSAElement(baseType), propertyName,
- QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
- QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
- getCurrentBindingSourceLocation()));
- }
+ if (m_passManager != nullptr)
+ generate_callProperty_SAcheck(propertyName, baseType);
addReadRegister(base, callBase);
@@ -1814,6 +1850,49 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
+void QQmlJSTypePropagator::generate_Construct_SCDate(int argc, int argv)
+{
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->dateTimeType()));
+
+ if (argc == 1) {
+ const QQmlJSRegisterContent argType = m_state.registers[argv].content;
+ if (m_typeResolver->isNumeric(argType)) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ } else if (m_typeResolver->registerContains(argType, m_typeResolver->stringType())) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->stringType()));
+ } else if (m_typeResolver->registerContains(argType, m_typeResolver->dateTimeType())
+ || m_typeResolver->registerContains(argType, m_typeResolver->dateType())
+ || m_typeResolver->registerContains(argType, m_typeResolver->timeType())) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->dateTimeType()));
+ } else {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+ } else {
+ constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
+ for (int i = 0; i < std::min(argc, maxArgc); ++i) {
+ addReadRegister(
+ argv + i, m_typeResolver->globalType(m_typeResolver->realType()));
+ }
+ }
+}
+
+void QQmlJSTypePropagator::generate_Construct_SCArray(int argc, int argv)
+{
+ if (argc == 1) {
+ if (m_typeResolver->isNumeric(m_state.registers[argv].content)) {
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->variantListType()));
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ } else {
+ generate_DefineArray(argc, argv);
+ }
+ } else {
+ generate_DefineArray(argc, argv);
+ }
+}
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
const QQmlJSRegisterContent type = m_state.registers[func].content;
@@ -1824,48 +1903,12 @@ void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
}
if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
- setAccumulator(m_typeResolver->globalType(m_typeResolver->dateTimeType()));
-
- if (argc == 1) {
- const QQmlJSRegisterContent argType = m_state.registers[argv].content;
- if (m_typeResolver->isNumeric(argType)) {
- addReadRegister(
- argv, m_typeResolver->globalType(m_typeResolver->realType()));
- } else if (m_typeResolver->registerContains(argType, m_typeResolver->stringType())) {
- addReadRegister(
- argv, m_typeResolver->globalType(m_typeResolver->stringType()));
- } else if (m_typeResolver->registerContains(argType, m_typeResolver->dateTimeType())
- || m_typeResolver->registerContains(argType, m_typeResolver->dateType())
- || m_typeResolver->registerContains(argType, m_typeResolver->timeType())) {
- addReadRegister(
- argv, m_typeResolver->globalType(m_typeResolver->dateTimeType()));
- } else {
- addReadRegister(
- argv, m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
- }
- } else {
- constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
- for (int i = 0; i < std::min(argc, maxArgc); ++i) {
- addReadRegister(
- argv + i, m_typeResolver->globalType(m_typeResolver->realType()));
- }
- }
-
+ generate_Construct_SCDate(argc, argv);
return;
}
if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
- if (argc == 1) {
- if (m_typeResolver->isNumeric(m_state.registers[argv].content)) {
- setAccumulator(m_typeResolver->globalType(m_typeResolver->variantListType()));
- addReadRegister(
- argv, m_typeResolver->globalType(m_typeResolver->realType()));
- } else {
- generate_DefineArray(argc, argv);
- }
- } else {
- generate_DefineArray(argc, argv);
- }
+ generate_Construct_SCArray(argc, argv);
return;
}
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index b4ce021be3..c9bbeb27cc 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -28,9 +28,10 @@ struct Q_QMLCOMPILER_EXPORT QQmlJSTypePropagator : public QQmlJSCompilePass
{
QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {},
QQmlSA::PassManager *passManager = nullptr);
- InstructionAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error);
void generate_Ret() override;
void generate_Debug() override;
@@ -242,6 +243,21 @@ private:
void recordEqualsType(int lhs);
void recordCompareType(int lhs);
+ // helper functions to deal with special cases in generate_ methods
+ void generate_CallProperty_SCMath(int base, int arcg, int argv);
+ void generate_CallProperty_SCconsole(int base, int argc, int argv);
+ void generate_Construct_SCDate(int argc, int argv);
+ void generate_Construct_SCArray(int argc, int argv);
+
+ // helper functions to perform QQmlSA checks
+ void generate_ret_SAcheck();
+ void generate_LoadQmlContextPropertyLookup_SAcheck(const QString &name);
+ void generate_StoreNameCommon_SAcheck(const QQmlJSRegisterContent &in, const QString &name);
+ void propagatePropertyLookup_SAcheck(const QString &propertyName);
+ void generate_StoreProperty_SAcheck(const QString propertyName, const QQmlJSRegisterContent &callBase);
+ void generate_callProperty_SAcheck(const QString propertyName, const QQmlJSScope::ConstPtr &baseType);
+
+
QQmlJSRegisterContent m_returnType;
QQmlSA::PassManager *m_passManager = nullptr;
QQmlJSScope::ConstPtr m_attachedContext;
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 75d617c026..93f9c7f7f4 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -31,6 +31,7 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
{
const QQmlJSImporter::ImportedTypes &builtinTypes = m_imports;
+
m_voidType = builtinTypes.type(u"void"_s).scope;
assertExtension(m_voidType, "undefined"_L1);
@@ -67,6 +68,12 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_uint64Type = builtinTypes.type(u"qulonglong"_s).scope;
Q_ASSERT(m_uint64Type);
+ m_sizeType = builtinTypes.type(u"qsizetype"_s).scope;
+ assertExtension(m_sizeType, "Number"_L1);
+
+ // qsizetype is either a 32bit or a 64bit signed integer. We don't want to special-case it.
+ Q_ASSERT(m_sizeType == m_int32Type || m_sizeType == m_int64Type);
+
m_boolType = builtinTypes.type(u"bool"_s).scope;
assertExtension(m_boolType, "Boolean"_L1);
@@ -181,6 +188,16 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
m_imports = visitor->imports();
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::mathObject() const
+{
+ return jsGlobalObject()->property(u"Math"_s).type();
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::consoleObject() const
+{
+ return jsGlobalObject()->property(u"console"_s).type();
+}
+
QQmlJSScope::ConstPtr
QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
{
@@ -345,18 +362,28 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const
{
- // Only types of length <= 32bit count as integral
return equals(type, m_int8Type)
|| equals(type, m_int16Type)
- || equals(type, m_int32Type);
+ || equals(type, m_int32Type)
+ || equals(type, m_int64Type);
}
bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
{
- // Only types of length <= 32bit count as integral
return equals(type, m_uint8Type)
|| equals(type, m_uint16Type)
- || equals(type, m_uint32Type);
+ || equals(type, m_uint32Type)
+ || equals(type, m_uint64Type);
+}
+
+bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
+{
+ return (equals(type, m_uint8Type)
+ || equals(type, m_int8Type)
+ || equals(type, m_uint16Type)
+ || equals(type, m_int16Type)
+ || equals(type, m_uint32Type)
+ || equals(type, m_int32Type));
}
QQmlJSScope::ConstPtr
@@ -1378,11 +1405,11 @@ QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
{
QQmlJSMetaProperty prop;
prop.setPropertyName(u"length"_s);
- prop.setTypeName(u"int"_s);
- prop.setType(int32Type());
+ prop.setTypeName(u"qsizetype"_s);
+ prop.setType(sizeType());
prop.setIsWritable(isWritable);
return QQmlJSRegisterContent::create(
- int32Type(), prop, QQmlJSRegisterContent::InvalidLookupIndex,
+ sizeType(), prop, QQmlJSRegisterContent::InvalidLookupIndex,
QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin, scope);
}
@@ -1595,7 +1622,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
return scope->valueType();
if (equals(scope, m_forInIteratorPtr))
- return m_int32Type;
+ return m_sizeType;
if (equals(scope, m_forOfIteratorPtr))
return list.scopeType()->valueType();
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 70ccf09fe4..9961c24842 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -54,6 +54,7 @@ public:
QQmlJSScope::ConstPtr uint32Type() const { return m_uint32Type; }
QQmlJSScope::ConstPtr int64Type() const { return m_int64Type; }
QQmlJSScope::ConstPtr uint64Type() const { return m_uint64Type; }
+ QQmlJSScope::ConstPtr sizeType() const { return m_sizeType; }
QQmlJSScope::ConstPtr boolType() const { return m_boolType; }
QQmlJSScope::ConstPtr stringType() const { return m_stringType; }
QQmlJSScope::ConstPtr stringListType() const { return m_stringListType; }
@@ -77,6 +78,9 @@ public:
QQmlJSScope::ConstPtr forInIteratorPtr() const { return m_forInIteratorPtr; }
QQmlJSScope::ConstPtr forOfIteratorPtr() const { return m_forOfIteratorPtr; }
+ QQmlJSScope::ConstPtr mathObject() const;
+ QQmlJSScope::ConstPtr consoleObject() const;
+
QQmlJSScope::ConstPtr scopeForLocation(const QV4::CompiledData::Location &location) const;
bool isPrefix(const QString &name) const
@@ -92,6 +96,11 @@ public:
{
return m_imports.type(name).scope;
}
+ QString nameForType(const QQmlJSScope::ConstPtr &type) const
+ {
+ return m_imports.name(originalType(type));
+ }
+
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
QQmlJSRegisterContent typeForBinaryOperation(QSOperator::Op oper,
@@ -197,6 +206,7 @@ public:
bool isIntegral(const QQmlJSScope::ConstPtr &type) const;
bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const;
bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const;
+ bool isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const;
bool canHold(const QQmlJSScope::ConstPtr &container,
const QQmlJSScope::ConstPtr &contained) const;
@@ -252,6 +262,7 @@ protected:
QQmlJSScope::ConstPtr m_uint32Type;
QQmlJSScope::ConstPtr m_int64Type;
QQmlJSScope::ConstPtr m_uint64Type;
+ QQmlJSScope::ConstPtr m_sizeType;
QQmlJSScope::ConstPtr m_boolType;
QQmlJSScope::ConstPtr m_stringType;
QQmlJSScope::ConstPtr m_stringListType;
diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h
index 9eb6840f9d..eaa834bec9 100644
--- a/src/qmlcompiler/qqmljsutils_p.h
+++ b/src/qmlcompiler/qqmljsutils_p.h
@@ -364,6 +364,14 @@ struct Q_QMLCOMPILER_EXPORT QQmlJSUtils
static std::variant<QString, QQmlJS::DiagnosticMessage>
sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath);
+
+ template <typename Container>
+ static void deduplicate(Container &container)
+ {
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+ }
};
bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(
diff --git a/src/qmldom/qqmldomexternalitems.cpp b/src/qmldom/qqmldomexternalitems.cpp
index 0ce3c9f5b4..6f48aa19e3 100644
--- a/src/qmldom/qqmldomexternalitems.cpp
+++ b/src/qmldom/qqmldomexternalitems.cpp
@@ -16,7 +16,6 @@
#include <QtCore/QDir>
#include <QtCore/QScopeGuard>
#include <QtCore/QFileInfo>
-#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
#include <algorithm>
@@ -604,9 +603,11 @@ bool QmlDirectory::iterateDirectSubpaths(const DomItem &self, DirectVisitor visi
bool QmlDirectory::addQmlFilePath(const QString &relativePath)
{
- QRegularExpression qmlFileRe(QRegularExpression::anchoredPattern(
- uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation))"));
- QRegularExpressionMatch m = qmlFileRe.match(relativePath);
+ static const QRegularExpression qmlFileRegularExpression{
+ QRegularExpression::anchoredPattern(
+ uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation|ui\.qml))")
+ };
+ QRegularExpressionMatch m = qmlFileRegularExpression.match(relativePath);
if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
m_qmlFiles.insert(m.captured(u"compName"), relativePath);
Export e;
diff --git a/src/qmldom/qqmldomexternalitems_p.h b/src/qmldom/qqmldomexternalitems_p.h
index 6b88efeca4..1aa9d765cb 100644
--- a/src/qmldom/qqmldomexternalitems_p.h
+++ b/src/qmldom/qqmldomexternalitems_p.h
@@ -25,6 +25,7 @@
#include <QtQml/private/qqmldirparser_p.h>
#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
#include <QtCore/QMetaType>
+#include <QtCore/qregularexpression.h>
#include <limits>
#include <memory>
diff --git a/src/qmldom/qqmldommoduleindex.cpp b/src/qmldom/qqmldommoduleindex.cpp
index 7bbc3c1bf9..d44c9ae003 100644
--- a/src/qmldom/qqmldommoduleindex.cpp
+++ b/src/qmldom/qqmldommoduleindex.cpp
@@ -297,8 +297,8 @@ ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
minorVersion = Version::Latest;
{
QMutexLocker l(mutex());
- auto it = m_moduleScope.find(minorVersion);
- if (it != m_moduleScope.end())
+ auto it = m_moduleScope.constFind(minorVersion);
+ if (it != m_moduleScope.cend())
return *it;
}
ModuleScope *res = nullptr;
@@ -306,8 +306,8 @@ ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
auto cleanup = qScopeGuard([&newScope] { delete newScope; });
{
QMutexLocker l(mutex());
- auto it = m_moduleScope.find(minorVersion);
- if (it != m_moduleScope.end()) {
+ auto it = m_moduleScope.constFind(minorVersion);
+ if (it != m_moduleScope.cend()) {
res = *it;
} else {
res = newScope;
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index 23e4a2f8cf..8ae836b0a2 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -1547,7 +1547,7 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(
auto &modsNow = m_moduleIndexWithUri[uri];
// As we do not hold the lock for the whole operation, some other thread
// might have created the module already
- if (auto it = modsNow.find(majorVersion); it != modsNow.end())
+ if (auto it = modsNow.constFind(majorVersion); it != modsNow.cend())
return *it;
modsNow.insert(majorVersion, newModulePtr);
}
diff --git a/src/qmlls/qqmllintsuggestions.cpp b/src/qmlls/qqmllintsuggestions.cpp
index abd66ec7d3..ab12195b4d 100644
--- a/src/qmlls/qqmllintsuggestions.cpp
+++ b/src/qmlls/qqmllintsuggestions.cpp
@@ -57,7 +57,7 @@ static void codeActionHandler(
int version = data[u"version"].toInt();
QJsonArray suggestions = data[u"suggestions"].toArray();
- QList<TextDocumentEdit> edits;
+ QList<WorkspaceEdit::DocumentChange> edits;
QString message;
for (const QJsonValue &suggestion : suggestions) {
QString replacement = suggestion[u"replacement"].toString();
diff --git a/src/qmlls/qqmlrenamesymbolsupport.cpp b/src/qmlls/qqmlrenamesymbolsupport.cpp
index a5d3ffa495..a812b5a25c 100644
--- a/src/qmlls/qqmlrenamesymbolsupport.cpp
+++ b/src/qmlls/qqmlrenamesymbolsupport.cpp
@@ -52,7 +52,8 @@ void QQmlRenameSymbolSupport::process(QQmlRenameSymbolSupport::RequestPointerArg
if (guard.setErrorFrom(QQmlLSUtils::checkNameForRename(front.domItem, newName, expressionType)))
return;
- QList<QLspSpecification::TextDocumentEdit> editsByFileForResult;
+ auto &editsByFileForResult = result.documentChanges.emplace();
+
// The QLspSpecification::WorkspaceEdit requires the changes to be grouped by files, so
// collect them into editsByFileUris.
QMap<QUrl, QList<QLspSpecification::TextEdit>> editsByFileUris;
@@ -102,8 +103,6 @@ void QQmlRenameSymbolSupport::process(QQmlRenameSymbolSupport::RequestPointerArg
}
editsByFileForResult.append(editsForCurrentFile);
}
-
- result.documentChanges = editsByFileForResult;
}
QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index cc0077db08..e24c10b786 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -351,26 +351,16 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel()
auto aim = m_adaptorModel.aim();
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsInserted(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)),
- q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QList<int>)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
-
+ QObject::connect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
+ QObject::connect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
+ QObject::connect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
+ QObject::connect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
+ QObject::connect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
+ QObject::connect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
+ QObject::connect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
+ QObject::connect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+ QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
@@ -381,25 +371,16 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
auto aim = m_adaptorModel.aim();
- QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
- q, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- q, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsInserted(QModelIndex,int,int)), q,
- SLOT(_q_columnsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsRemoved(QModelIndex,int,int)), q,
- SLOT(_q_columnsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), q,
- SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int)));
- QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)),
- q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QList<int>)));
- QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::disconnect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
+ QObject::disconnect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
- QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+ QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModel::setModel(const QVariant &model)
diff --git a/src/qmlmodels/qqmldmabstractitemmodeldata.cpp b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp
index 901d8bbe53..bf4a0226b1 100644
--- a/src/qmlmodels/qqmldmabstractitemmodeldata.cpp
+++ b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp
@@ -67,8 +67,8 @@ void QQmlDMAbstractItemModelData::setValue(const QString &role, const QVariant &
return;
}
- QHash<QByteArray, int>::iterator it = m_type->roleNames.find(role.toUtf8());
- if (it != m_type->roleNames.end()) {
+ const auto it = m_type->roleNames.constFind(role.toUtf8());
+ if (it != m_type->roleNames.cend()) {
for (int i = 0; i < m_type->propertyRoles.size(); ++i) {
if (m_type->propertyRoles.at(i) == *it) {
m_cachedData[i] = value;
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel.cpp b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
index 792fb53f56..db128761cd 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel.cpp
+++ b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
@@ -26,53 +26,52 @@ QAbstractItemModel *QQmlTreeModelToTableModel::model() const
return m_model;
}
-void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
+void QQmlTreeModelToTableModel::connectToModel()
{
- struct Cx {
- const char *signal;
- const char *slot;
- };
- const Cx connections[] = {
- { SIGNAL(destroyed(QObject*)),
- SLOT(modelHasBeenDestroyed()) },
- { SIGNAL(modelReset()),
- SLOT(modelHasBeenReset()) },
- { SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)),
- SLOT(modelDataChanged(QModelIndex,QModelIndex,QList<int>)) },
-
- { SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- SLOT(modelLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
- { SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- SLOT(modelLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
-
- { SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
- SLOT(modelRowsAboutToBeInserted(QModelIndex,int,int)) },
- { SIGNAL(rowsInserted(QModelIndex,int,int)),
- SLOT(modelRowsInserted(QModelIndex,int,int)) },
- { SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- SLOT(modelRowsAboutToBeRemoved(QModelIndex,int,int)) },
- { SIGNAL(rowsRemoved(QModelIndex,int,int)),
- SLOT(modelRowsRemoved(QModelIndex,int,int)) },
- { SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
- SLOT(modelRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) },
- { SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- SLOT(modelRowsMoved(QModelIndex,int,int,QModelIndex,int)) },
-
- { SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
- SLOT(modelColumnsAboutToBeInserted(QModelIndex,int,int))},
- { SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
- SLOT(modelColumnsAboutToBeRemoved(QModelIndex,int,int))},
- { SIGNAL(columnsInserted(QModelIndex,int,int)),
- SLOT(modelColumnsInserted(QModelIndex,int,int))},
- { SIGNAL(columnsRemoved(QModelIndex,int,int)),
- SLOT(modelColumnsRemoved(QModelIndex,int,int))},
- { nullptr, nullptr }
+ m_connections = {
+ QObject::connect(m_model, &QAbstractItemModel::destroyed,
+ this, &QQmlTreeModelToTableModel::modelHasBeenDestroyed),
+ QObject::connect(m_model, &QAbstractItemModel::modelReset,
+ this, &QQmlTreeModelToTableModel::modelHasBeenReset),
+ QObject::connect(m_model, &QAbstractItemModel::dataChanged,
+ this, &QQmlTreeModelToTableModel::modelDataChanged),
+
+ QObject::connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
+ this, &QQmlTreeModelToTableModel::modelLayoutAboutToBeChanged),
+ QObject::connect(m_model, &QAbstractItemModel::layoutChanged,
+ this, &QQmlTreeModelToTableModel::modelLayoutChanged),
+
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeInserted),
+ QObject::connect(m_model, &QAbstractItemModel::rowsInserted,
+ this, &QQmlTreeModelToTableModel::modelRowsInserted),
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsRemoved,
+ this, &QQmlTreeModelToTableModel::modelRowsRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeMoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsMoved,
+ this, &QQmlTreeModelToTableModel::modelRowsMoved),
+
+ QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
+ this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeInserted),
+ QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
+ this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::columnsInserted,
+ this, &QQmlTreeModelToTableModel::modelColumnsInserted),
+ QObject::connect(m_model, &QAbstractItemModel::columnsRemoved,
+ this, &QQmlTreeModelToTableModel::modelColumnsRemoved)
};
+}
+void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
+{
if (m_model != arg) {
if (m_model) {
- for (const Cx *c = &connections[0]; c->signal; c++)
- disconnect(m_model, c->signal, this, c->slot);
+ for (const auto &c : m_connections)
+ QObject::disconnect(c);
+ m_connections.fill({});
}
clearModelData();
@@ -82,9 +81,7 @@ void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
m_rootIndex = QModelIndex();
if (m_model) {
- for (const Cx *c = &connections[0]; c->signal; c++)
- connect(m_model, c->signal, this, c->slot);
-
+ connectToModel();
showModelTopLevelItems();
}
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
index aefc954cff..209977f48b 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
+++ b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
@@ -162,6 +162,7 @@ private:
const QModelIndex &bottomRight,
const QVector<int> &roles);
void emitQueuedSignals();
+ void connectToModel();
QPointer<QAbstractItemModel> m_model = nullptr;
QPersistentModelIndex m_rootIndex;
@@ -173,6 +174,7 @@ private:
bool m_modelLayoutChanged = false;
int m_signalAggregatorStack = 0;
QVector<DataChangedParams> m_queuedDataChanged;
+ std::array<QMetaObject::Connection, 15> m_connections;
int m_column = 0;
};
diff --git a/src/qmltoolingsettings/qqmltoolingsettings.cpp b/src/qmltoolingsettings/qqmltoolingsettings.cpp
index 3f5aecccfb..5f8b92a8d2 100644
--- a/src/qmltoolingsettings/qqmltoolingsettings.cpp
+++ b/src/qmltoolingsettings/qqmltoolingsettings.cpp
@@ -39,6 +39,7 @@ bool QQmlToolingSettings::read(const QString &settingsFilePath)
return true;
#else
+ Q_UNUSED(settingsFilePath);
return false;
#endif
}
@@ -118,6 +119,7 @@ bool QQmlToolingSettings::search(const QString &path)
m_seenDirectories[dir] = QString();
#endif
+ Q_UNUSED(path);
return false;
}
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
index dab931254a..480d4ba187 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -25,6 +25,9 @@ using namespace QAnyStringViewUtils;
const MetaTypePrivate MetaType::s_empty;
+// TODO: This could be optimized to store the objects in a more compact way.
+std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
+
static QCborValue fromJson(const QByteArray &json, QJsonParseError *error)
{
const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
@@ -50,15 +53,6 @@ QList<QAnyStringView> MetaTypesJsonProcessor::namespaces(const MetaType &classDe
return namespaces;
}
-MetaTypesJsonProcessor::~MetaTypesJsonProcessor()
-{
- for (const MetaType &type : m_types)
- delete type.d;
-
- for (const MetaType &type : m_foreignTypes)
- delete type.d;
-}
-
bool MetaTypesJsonProcessor::processTypes(const QStringList &files)
{
for (const QString &source: files) {
@@ -152,14 +146,15 @@ static void sortStringList(QList<String> *list)
void MetaTypesJsonProcessor::postProcessTypes()
{
sortTypes(m_types);
- sortStringList(&m_includes);
}
void MetaTypesJsonProcessor::postProcessForeignTypes()
{
sortTypes(m_foreignTypes);
+ sortStringList(&m_primitiveTypes);
addRelatedTypes();
sortStringList(&m_referencedTypes);
+ sortStringList(&m_includes);
}
QString MetaTypesJsonProcessor::extractRegisteredTypes() const
@@ -225,30 +220,63 @@ QString MetaTypesJsonProcessor::extractRegisteredTypes() const
return registrationHelper;
}
-MetaTypesJsonProcessor::RegistrationMode MetaTypesJsonProcessor::qmlTypeRegistrationMode(
- const MetaType &classDef)
+MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess(
+ const MetaType &classDef, PopulateMode populateMode)
{
- for (const ClassInfo &entry : classDef.classInfos()) {
- const QAnyStringView name = entry.name;
- if (name == S_ELEMENT) {
+ // If this type is a self-extending value type or a sequence type or has a JavaScript extension
+ // and is not the root object, then it's foreign type has no entry of its own.
+ // In that case we need to generate a "primitive" entry.
+
+ QList<QAnyStringView> aliases;
+ QAnyStringView foreign;
+ RegistrationMode mode = NoRegistration;
+ bool isSelfExtendingValueType = false;
+ bool hasJavaScriptExtension = false;
+ bool isRootObject = false;
+ bool isSequence = false;
+
+ for (const ClassInfo &classInfo : classDef.classInfos()) {
+ if (classInfo.name == S_FOREIGN)
+ foreign = classInfo.value;
+ else if (classInfo.name == S_PRIMITIVE_ALIAS)
+ aliases.append(classInfo.value);
+ else if (classInfo.name == S_EXTENSION_IS_JAVA_SCRIPT)
+ hasJavaScriptExtension = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_EXTENDED && classDef.kind() == MetaType::Kind::Gadget)
+ isSelfExtendingValueType = classInfo.value == classDef.className();
+ else if (classInfo.name == S_ROOT)
+ isRootObject = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_SEQUENCE)
+ isSequence = true;
+ else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) {
switch (classDef.kind()) {
case MetaType::Kind::Object:
- return ObjectRegistration;
+ mode = ObjectRegistration;
+ break;
case MetaType::Kind::Gadget:
- return GadgetRegistration;
+ mode = GadgetRegistration;
+ break;
case MetaType::Kind::Namespace:
- return NamespaceRegistration;
+ mode = NamespaceRegistration;
+ break;
default:
warning(classDef)
- << "Not registering a classInfo which is neither an object,"
- << "nor a gadget, nor a namespace:"
- << name.toString();
+ << "Not registering a classInfo which is neither an object,"
+ << "nor a gadget, nor a namespace:"
+ << classInfo.name.toString();
break;
}
- break;
}
}
- return NoRegistration;
+
+ return PreProcessResult {
+ aliases,
+ (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
+ ? foreign
+ : QAnyStringView(),
+ mode
+ };
+
}
// TODO: Remove this when QAnyStringView gets a proper qHash()
@@ -357,6 +385,11 @@ void MetaTypesJsonProcessor::addRelatedTypes()
qualifiedClassNameLessThan);
m_types.insert(insert, type);
+ // Also add its include. We don't want to rely on transitive inclues.
+ const QString inputFile = type.inputFile();
+ if (!inputFile.isEmpty())
+ m_includes.append(inputFile);
+
// Remove from the foreign types to avoid the ODR warning.
const auto remove = std::equal_range(
m_foreignTypes.constBegin(), m_foreignTypes.constEnd(), type,
@@ -369,7 +402,7 @@ void MetaTypesJsonProcessor::addRelatedTypes()
}
};
- const auto addInterfaceOrSelfExtension
+ const auto addInterface
= [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces) {
if (const FoundType other = QmlTypesClassDescription::findType(
m_types, m_foreignTypes, typeName, namespaces)) {
@@ -388,7 +421,7 @@ void MetaTypesJsonProcessor::addRelatedTypes()
};
const auto addType = [&](const MetaType &context, QAnyStringView typeName,
- const QList<QAnyStringView> &namespaces) {
+ const QList<QAnyStringView> &namespaces, QAnyStringView relation) {
if (const FoundType other = QmlTypesClassDescription::findType(
m_types, m_foreignTypes, typeName, namespaces)) {
addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
@@ -405,7 +438,7 @@ void MetaTypesJsonProcessor::addRelatedTypes()
const QAnyStringView enumName = typeName.mid(index + separator.length());
for (const Enum &enumerator : other.native.enums()) {
- if (enumerator.name != enumName)
+ if (enumerator.name != enumName && enumerator.alias != enumName)
continue;
addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
@@ -417,20 +450,40 @@ void MetaTypesJsonProcessor::addRelatedTypes()
}
}
+ // If it's an enum of the context type itself, we don't have to do anything.
+ for (const Enum &enumerator : context.enums()) {
+ if (enumerator.name == typeName || enumerator.alias == typeName)
+ return true;
+ }
+
// If we've detected this type as unresolved foreign and it actually belongs to this module,
// we'll get to it again when we process it as foreign type. In that case we'll look at the
// special cases for sequences and extensions.
- if (!unresolvedForeignNames.contains(typeName))
- warning(context) << typeName << "is used but cannot be found.";
+ if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName))
+ warning(context) << typeName << "is used as" << relation << "type but cannot be found.";
processedRelatedNativeNames.insert(typeName);
processedRelatedJavaScriptNames.insert(typeName);
return false;
};
+ const auto doAddReferences = [&](QAnyStringView typeName,
+ const QList<QAnyStringView> &namespaces) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ addReference(
+ other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(
+ other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ return true;
+ }
+
+ return false;
+ };
+
const auto addSupers = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) {
for (const Interface &iface : context.ifaces())
- addInterfaceOrSelfExtension(interfaceName(iface), namespaces);
+ addInterface(interfaceName(iface), namespaces);
// We don't warn about missing bases for value types. They don't have to be registered.
bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
@@ -442,110 +495,128 @@ void MetaTypesJsonProcessor::addRelatedTypes()
continue;
QAnyStringView typeName = superObject.name;
- if (const FoundType other = QmlTypesClassDescription::findType(
- m_types, m_foreignTypes, typeName, namespaces)) {
- addReference(
- other.native, &processedRelatedNativeNames, other.nativeOrigin);
- addReference(
- other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ if (doAddReferences(typeName, namespaces))
warnAboutSupers = false;
- } else {
+ else
missingSupers.append(typeName);
- }
}
for (QAnyStringView typeName : std::as_const(missingSupers)) {
// If we've found one valid base type, don't complain about the others.
- if (warnAboutSupers && !unresolvedForeignNames.contains(typeName))
- warning(context) << typeName << "is used but cannot be found.";
+ if (warnAboutSupers
+ && !unresolvedForeignNames.contains(typeName)
+ && !isPrimitive(typeName)) {
+ warning(context) << typeName << "is used as base type but cannot be found.";
+ }
processedRelatedNativeNames.insert(typeName);
processedRelatedJavaScriptNames.insert(typeName);
}
};
+ const auto addProperties = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Property &property : context.properties()) {
+ ResolvedTypeAlias resolved(property.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "property");
+ }
+ };
+
+ const auto addMethods = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Method::Container &methods
+ : {context.methods(), context.constructors(), context.sigs() }) {
+ for (const Method &methodObject : methods) {
+ for (const Argument &argument : std::as_const(methodObject.arguments)) {
+ ResolvedTypeAlias resolved(argument.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "argument");
+ }
+
+ ResolvedTypeAlias resolved(methodObject.returnType);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "return");
+ }
+ }
+ };
+
+ const auto addEnums = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Enum &enumerator : context.enums()) {
+ ResolvedTypeAlias resolved(enumerator.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "enum");
+ }
+ };
+
+ const auto addRelation = [&](const MetaType &classDef, const ClassInfo &obj,
+ const QList<QAnyStringView> &namespaces) {
+ const QAnyStringView objNameValue = obj.name;
+ if (objNameValue == S_ATTACHED) {
+ addType(classDef, obj.value, namespaces, "attached");
+ return true;
+ } else if (objNameValue == S_SEQUENCE) {
+ ResolvedTypeAlias value(obj.value);
+ addType(classDef, value.type, namespaces, "sequence value");
+ return true;
+ } else if (objNameValue == S_EXTENDED) {
+ const QAnyStringView value = obj.value;
+ addType(classDef, value, namespaces, "extension");
+ return true;
+ }
+ return false;
+ };
+
// Then recursively iterate the super types and attached types, marking the
// ones we are interested in as related.
while (!typeQueue.isEmpty()) {
QAnyStringView unresolvedForeign;
- // We don't need to resolve the foreign part of sequence registrations.
- bool isSequence = false;
- // We don't need to resolve the foreign part of self-extending value types.
- bool isSelfExtendingValueType = false;
- // We don't want to deal with builtins that have JavaScript extensions. Consider them found.
- bool hasJavaScriptExtension = false;
-
const MetaType classDef = typeQueue.dequeue();
const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
for (const ClassInfo &obj : classDef.classInfos()) {
- const QAnyStringView objNameValue = obj.name;
- if (objNameValue == S_ATTACHED) {
- addType(classDef, obj.value, namespaces);
- } else if (objNameValue == S_SEQUENCE) {
- isSequence = true;
- QAnyStringView value = obj.value;
-
- if (!value.isEmpty() && value.back() == '*'_L1) {
- // Pointers as sequence values include the '*'
- QAnyStringView chopped = value.chopped(1);
- while (!chopped.isEmpty() && chopped.back() == ' '_L1)
- chopped = chopped.chopped(1);
- addType(classDef, chopped, namespaces);
- } else {
- addType(classDef, value, namespaces);
- }
- } else if (objNameValue == S_EXTENDED) {
- const QAnyStringView value = obj.value;
- if (value == classDef.qualifiedClassName()
- && classDef.kind() == MetaType::Kind::Gadget) {
- isSelfExtendingValueType = true;
- addInterfaceOrSelfExtension(value, namespaces);
- } else {
- addType(classDef, value, namespaces);
- }
- } else if (objNameValue == S_EXTENSION_IS_JAVA_SCRIPT) {
- hasJavaScriptExtension = true;
- } else if (objNameValue == S_FOREIGN) {
- const QAnyStringView foreignClassName = obj.value;
-
- // A type declared as QML_FOREIGN will usually be a foreign type, but it can
- // actually be an additional registration of a local type, too.
- const FoundType found = QmlTypesClassDescription::findType(
- m_foreignTypes, {}, foreignClassName, namespaces);
- if (!found) {
- if (!QmlTypesClassDescription::findType(
- m_types, {}, foreignClassName, namespaces)) {
- unresolvedForeign = foreignClassName;
- }
- } else {
- const MetaType other = found.select(classDef, "Foreign");
- const QList<QAnyStringView> otherNamespaces
- = MetaTypesJsonProcessor::namespaces(other);
- addSupers(other, otherNamespaces);
-
- for (const ClassInfo &obj : other.classInfos()) {
- const QAnyStringView objNameValue = obj.name;
- if (objNameValue == S_ATTACHED || objNameValue == S_SEQUENCE
- || objNameValue == S_EXTENDED) {
- addType(classDef, obj.value, otherNamespaces);
- break;
- }
- // No, you cannot chain S_FOREIGN declarations. Sorry.
- }
+ if (addRelation(classDef, obj, namespaces))
+ continue;
+ if (obj.name != S_FOREIGN)
+ continue;
+
+ const QAnyStringView foreignClassName = obj.value;
+
+ // A type declared as QML_FOREIGN will usually be a foreign type, but it can
+ // actually be an additional registration of a local type, too.
+ if (const FoundType found = QmlTypesClassDescription::findType(
+ m_foreignTypes, {}, foreignClassName, namespaces)) {
+ const MetaType other = found.select(classDef, "Foreign");
+ const QList<QAnyStringView> otherNamespaces
+ = MetaTypesJsonProcessor::namespaces(other);
+ addSupers(other, otherNamespaces);
+ addProperties(other, otherNamespaces);
+ addMethods(other, otherNamespaces);
+ addEnums(other, otherNamespaces);
+
+ for (const ClassInfo &obj : other.classInfos()) {
+ if (addRelation(classDef, obj, otherNamespaces))
+ break;
+ // No, you cannot chain S_FOREIGN declarations. Sorry.
}
+ } else if (!QmlTypesClassDescription::findType(
+ m_types, {}, foreignClassName, namespaces)) {
+ unresolvedForeign = foreignClassName;
}
}
- if (!isSequence && !isSelfExtendingValueType && !hasJavaScriptExtension
- && !unresolvedForeign.isEmpty()) {
+ if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
warning(classDef)
<< unresolvedForeign
<< "is declared as foreign type, but cannot be found.";
}
addSupers(classDef, namespaces);
+ addProperties(classDef, namespaces);
+ addMethods(classDef, namespaces);
+ addEnums(classDef, namespaces);
}
}
@@ -556,9 +627,16 @@ void MetaTypesJsonProcessor::sortTypes(QVector<MetaType> &types)
QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
{
- return (m_privateIncludes && endsWith(include, "_p.h"_L1))
- ? QLatin1String("private/") + include.toString()
- : include.toString();
+ if (!m_privateIncludes)
+ return include.toString();
+
+ if (endsWith(include, "_p.h"_L1))
+ return QLatin1String("private/") + include.toString();
+
+ if (startsWith(include, "qplatform"_L1) || startsWith(include, "qwindowsystem"_L1))
+ return QLatin1String("qpa/") + include.toString();
+
+ return include.toString();
}
void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
@@ -568,7 +646,8 @@ void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
for (const QCborValue &cls : classes) {
const MetaType classDef(cls.toMap(), include);
- switch (qmlTypeRegistrationMode(classDef)) {
+ const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
+ switch (preprocessed.mode) {
case NamespaceRegistration:
case GadgetRegistration:
case ObjectRegistration: {
@@ -591,6 +670,11 @@ void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
m_foreignTypes.emplaceBack(classDef);
break;
}
+
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
}
}
@@ -598,8 +682,16 @@ void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types)
{
const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
const QCborArray classes = types[S_CLASSES].toArray();
- for (const QCborValue &cls : classes)
- m_foreignTypes.emplaceBack(cls.toMap(), include);
+ for (const QCborValue &cls : classes) {
+ const MetaType classDef(cls.toMap(), include);
+ PreProcessResult preprocessed = preProcess(classDef, PopulateMode::No);
+
+ m_foreignTypes.emplaceBack(classDef);
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
+ }
}
static QTypeRevision getRevision(const QCborMap &cbor)
@@ -748,4 +840,8 @@ MetaTypePrivate::MetaTypePrivate(const QCborMap &cbor, const QString &inputFile)
kind = Kind::Namespace;
}
+MetaType::MetaType(const QCborMap &cbor, const QString &inputFile)
+ : d(s_pool.emplace_back(std::make_unique<MetaTypePrivate>(cbor, inputFile)).get())
+{}
+
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
index c370514e9b..9d86665c52 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
@@ -174,13 +174,11 @@ public:
using Kind = MetaTypePrivate::Kind;
MetaType() = default;
- MetaType(const QCborMap &cbor, const QString &inputFile)
- : d(new MetaTypePrivate(cbor, inputFile))
- {}
+ MetaType(const QCborMap &cbor, const QString &inputFile);
bool isEmpty() const { return d == &s_empty; }
- QAnyStringView inputFile() const { return d->inputFile; }
+ QString inputFile() const { return d->inputFile; }
QAnyStringView className() const { return d->className; }
QAnyStringView qualifiedClassName() const { return d->qualifiedClassName; }
const BaseType::Container &superClasses() const { return d->superClasses; }
@@ -197,8 +195,6 @@ public:
Kind kind() const { return d->kind; }
private:
- friend class MetaTypesJsonProcessor;
-
friend bool operator==(const MetaType &a, const MetaType &b) noexcept
{
return a.d == b.d;
@@ -219,7 +215,6 @@ public:
static QList<QAnyStringView> namespaces(const MetaType &classDef);
MetaTypesJsonProcessor(bool privateIncludes) : m_privateIncludes(privateIncludes) {}
- ~MetaTypesJsonProcessor();
bool processTypes(const QStringList &files);
@@ -244,7 +239,19 @@ private:
NamespaceRegistration
};
- static RegistrationMode qmlTypeRegistrationMode(const MetaType &classDef);
+ struct PreProcessResult {
+ QList<QAnyStringView> primitiveAliases;
+ QAnyStringView foreignPrimitive;
+ RegistrationMode mode;
+ };
+
+ struct PotentialPrimitiveType {
+ QAnyStringView name;
+ QString file;
+ };
+
+ enum class PopulateMode { No, Yes };
+ static PreProcessResult preProcess(const MetaType &classDef, PopulateMode populateMode);
void addRelatedTypes();
void sortTypes(QVector<MetaType> &types);
@@ -252,8 +259,14 @@ private:
void processTypes(const QCborMap &types);
void processForeignTypes(const QCborMap &types);
+ bool isPrimitive(QAnyStringView type) const
+ {
+ return std::binary_search(m_primitiveTypes.begin(), m_primitiveTypes.end(), type);
+ }
+
QList<QString> m_includes;
QList<QAnyStringView> m_referencedTypes;
+ QList<QAnyStringView> m_primitiveTypes;
QVector<MetaType> m_types;
QVector<MetaType> m_foreignTypes;
bool m_privateIncludes = false;
diff --git a/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h
index 6b58e87357..465cc23532 100644
--- a/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h
+++ b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h
@@ -25,6 +25,7 @@ namespace Constants {
namespace DotQmltypes {
static constexpr QLatin1StringView S_ACCESS_SEMANTICS { "accessSemantics" };
static constexpr QLatin1StringView S_ALIAS { "alias" };
+static constexpr QLatin1StringView S_ALIASES { "aliases" };
static constexpr QLatin1StringView S_ARGUMENTS { "arguments" };
static constexpr QLatin1StringView S_ATTACHED_TYPE { "attachedType" };
static constexpr QLatin1StringView S_BINDABLE { "bindable" };
@@ -156,6 +157,7 @@ static constexpr QLatin1StringView S_EXTENSION_IS_NAMESPACE { "QML.Extensi
static constexpr QLatin1StringView S_FOREIGN { "QML.Foreign" };
static constexpr QLatin1StringView S_FOREIGN_IS_NAMESPACE { "QML.ForeignIsNamespace" };
static constexpr QLatin1StringView S_HAS_CUSTOM_PARSER { "QML.HasCustomParser" };
+static constexpr QLatin1StringView S_PRIMITIVE_ALIAS { "QML.PrimitiveAlias" };
static constexpr QLatin1StringView S_REMOVED_IN_VERSION { "QML.RemovedInVersion" };
static constexpr QLatin1StringView S_ROOT { "QML.Root" };
static constexpr QLatin1StringView S_SEQUENCE { "QML.Sequence" };
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
index 2f1f268dc8..7c42052a70 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
@@ -12,6 +12,7 @@
#include <QtCore/qcbormap.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
using namespace Constants;
using namespace Constants::MetatypesDotJson;
using namespace Constants::MetatypesDotJson::Qml;
@@ -286,6 +287,8 @@ void QmlTypesClassDescription::collect(
foreignTypeName = value;
} else if (name == S_FOREIGN_IS_NAMESPACE) {
foreignIsNamespace = (value == S_TRUE);
+ } else if (name == S_PRIMITIVE_ALIAS) {
+ primitiveAliases.append(value);
} else if (name == S_ROOT) {
isRootClass = (value == S_TRUE);
} else if (name == S_HAS_CUSTOM_PARSER) {
@@ -454,4 +457,49 @@ FoundType QmlTypesClassDescription::collectRelated(
return FoundType();
}
+
+ResolvedTypeAlias::ResolvedTypeAlias(QAnyStringView alias)
+ : type(alias)
+{
+ if (type.isEmpty())
+ return;
+
+ if (type == "void") {
+ type = "";
+ return;
+ }
+
+ // This is a best effort approach and will not return correct results in the
+ // presence of typedefs.
+
+ auto handleList = [&](QLatin1StringView list) {
+ if (!startsWith(type, list) || type.back() != '>'_L1)
+ return false;
+
+ const int listSize = list.size();
+ const QAnyStringView elementType = trimmed(type.mid(listSize, type.size() - listSize - 1));
+
+ // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will
+ // produce double pointers. QList is only for value types. We can't handle QLists
+ // of pointers (unless specially registered, but then they're not isList).
+ if (elementType.back() == '*'_L1)
+ return false;
+
+ isList = true;
+ type = elementType;
+ return true;
+ };
+
+ if (!handleList("QQmlListProperty<"_L1) && !handleList("QList<"_L1)) {
+ if (type.back() == '*'_L1) {
+ isPointer = true;
+ type = type.chopped(1);
+ }
+ if (startsWith(type, "const "_L1)) {
+ isConstant = true;
+ type = type.sliced(strlen("const "));
+ }
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
index 721b7f16dd..60aafb948f 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h
+++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
@@ -56,6 +56,7 @@ struct QmlTypesClassDescription
MetaType resolvedClass;
QAnyStringView file;
QAnyStringView className;
+ QList<QAnyStringView> primitiveAliases;
QList<QAnyStringView> elementNames;
QAnyStringView defaultProp;
QAnyStringView parentProp;
@@ -110,5 +111,15 @@ private:
void collectInterfaces(const MetaType &classDef);
};
+struct ResolvedTypeAlias
+{
+ ResolvedTypeAlias(QAnyStringView alias);
+
+ QAnyStringView type;
+ bool isList = false;
+ bool isPointer = false;
+ bool isConstant = false;
+};
+
QT_END_NAMESPACE
#endif // QMLTYPESCLASSDESCRIPTION_P_H
diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp
index fb07ff2a48..297a23679a 100644
--- a/src/qmltyperegistrar/qqmltypescreator.cpp
+++ b/src/qmltyperegistrar/qqmltypescreator.cpp
@@ -38,6 +38,9 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
m_qml.writeStringBinding(S_FILE, collector.file);
m_qml.writeStringBinding(S_NAME, collector.className);
+ if (!collector.primitiveAliases.isEmpty())
+ m_qml.writeStringListBinding(S_ALIASES, collector.primitiveAliases);
+
if (!collector.accessSemantics.isEmpty())
m_qml.writeStringBinding(S_ACCESS_SEMANTICS, collector.accessSemantics);
@@ -160,77 +163,16 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
void QmlTypesCreator::writeType(QAnyStringView type)
{
- if (type.isEmpty() || type == "void")
+ ResolvedTypeAlias resolved(type);
+ if (resolved.type.isEmpty())
return;
- bool isList = false;
- bool isPointer = false;
- // This is a best effort approach (like isPointer) and will not return correct results in the
- // presence of typedefs.
- bool isConstant = false;
-
- auto handleList = [&](QLatin1StringView list) {
- if (!startsWith(type, list) || type.back() != '>'_L1)
- return false;
-
- const int listSize = list.size();
- const QAnyStringView elementType = trimmed(type.mid(listSize, type.size() - listSize - 1));
-
- // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will
- // produce double pointers. QList is only for value types. We can't handle QLists
- // of pointers (unless specially registered, but then they're not isList).
- if (elementType.back() == '*'_L1)
- return false;
-
- isList = true;
- type = elementType;
- return true;
- };
-
- if (!handleList("QQmlListProperty<"_L1) && !handleList("QList<"_L1)) {
- if (type.back() == '*'_L1) {
- isPointer = true;
- type = type.chopped(1);
- }
- if (startsWith(type, "const "_L1)) {
- isConstant = true;
- type = type.sliced(strlen("const "));
- }
- }
-
- if (type == "qreal") {
-#ifdef QT_COORD_TYPE_STRING
- type = QT_COORD_TYPE_STRING;
-#else
- type = "double";
-#endif
- } else if (type == "int8_t") {
- // TODO: What can we do with "char"? It's ambiguous.
- type = "qint8";
- } else if (type == "uchar" || type == "uint8_t") {
- type = "quint8";
- } else if (type == "qint16" || type == "int16_t") {
- type = "short";
- } else if (type == "quint16" || type == "uint16_t") {
- type = "ushort";
- } else if (type == "qint32" || type == "int32_t") {
- type = "int";
- } else if (type == "quint32" || type == "uint32_t") {
- type = "uint";
- } else if (type == "qint64" || type == "int64_t") {
- type = "qlonglong";
- } else if (type == "quint64" || type == "uint64_t") {
- type = "qulonglong";
- } else if (type == "QList<QObject*>") {
- type = "QObjectList";
- }
-
- m_qml.writeStringBinding(S_TYPE, type);
- if (isList)
+ m_qml.writeStringBinding(S_TYPE, resolved.type);
+ if (resolved.isList)
m_qml.writeBooleanBinding(S_IS_LIST, true);
- if (isPointer)
+ if (resolved.isPointer)
m_qml.writeBooleanBinding(S_IS_POINTER, true);
- if (isConstant)
+ if (resolved.isConstant)
m_qml.writeBooleanBinding(S_IS_CONSTANT, true);
}
@@ -428,36 +370,41 @@ void QmlTypesCreator::writeRootMethods(const MetaType &classDef)
writeMethods(componentMethods, S_METHOD);
};
-void QmlTypesCreator::writeComponents()
+void QmlTypesCreator::writeComponent(const QmlTypesClassDescription &collector)
{
- for (const MetaType &component : std::as_const(m_ownTypes)) {
- QmlTypesClassDescription collector;
- collector.collect(component, m_ownTypes, m_foreignTypes,
- QmlTypesClassDescription::TopLevel, m_version);
+ m_qml.writeStartObject(S_COMPONENT);
- m_qml.writeStartObject(S_COMPONENT);
+ writeClassProperties(collector);
- writeClassProperties(collector);
+ if (const MetaType &classDef = collector.resolvedClass; !classDef.isEmpty()) {
+ writeEnums(
+ classDef.enums(),
+ collector.registerEnumClassesScoped
+ ? EnumClassesMode::Scoped
+ : EnumClassesMode::Unscoped);
- if (const MetaType &classDef = collector.resolvedClass; !classDef.isEmpty()) {
- writeEnums(
- classDef.enums(),
- collector.registerEnumClassesScoped
- ? EnumClassesMode::Scoped
- : EnumClassesMode::Unscoped);
+ writeProperties(members(classDef.properties(), m_version));
- writeProperties(members(classDef.properties(), m_version));
+ if (collector.isRootClass) {
+ writeRootMethods(classDef);
+ } else {
+ writeMethods(members(classDef.sigs(), m_version), S_SIGNAL);
+ writeMethods(members(classDef.methods(), m_version), S_METHOD);
+ }
- if (collector.isRootClass) {
- writeRootMethods(classDef);
- } else {
- writeMethods(members(classDef.sigs(), m_version), S_SIGNAL);
- writeMethods(members(classDef.methods(), m_version), S_METHOD);
- }
+ writeMethods(constructors(classDef.constructors(), m_version), S_METHOD);
+ }
+ m_qml.writeEndObject();
+}
- writeMethods(constructors(classDef.constructors(), m_version), S_METHOD);
- }
- m_qml.writeEndObject();
+void QmlTypesCreator::writeComponents()
+{
+ for (const MetaType &component : std::as_const(m_ownTypes)) {
+ QmlTypesClassDescription collector;
+ collector.collect(component, m_ownTypes, m_foreignTypes,
+ QmlTypesClassDescription::TopLevel, m_version);
+
+ writeComponent(collector);
if (collector.resolvedClass != component
&& std::binary_search(
@@ -468,25 +415,11 @@ void QmlTypesCreator::writeComponents()
// also generate a description of the local type then. All the QML_* macros are
// ignored, and the result is an anonymous type.
- m_qml.writeStartObject(S_COMPONENT);
-
QmlTypesClassDescription collector;
collector.collectLocalAnonymous(component, m_ownTypes, m_foreignTypes, m_version);
+ Q_ASSERT(!collector.isRootClass);
- writeClassProperties(collector);
- writeEnums(
- component.enums(),
- collector.registerEnumClassesScoped
- ? EnumClassesMode::Scoped
- : EnumClassesMode::Unscoped);
-
- writeProperties(members(component.properties(), m_version));
-
- writeMethods(members(component.sigs(), m_version), S_SIGNAL);
- writeMethods(members(component.methods(), m_version), S_METHOD);
- writeMethods(constructors(component.constructors(), m_version), S_METHOD);
-
- m_qml.writeEndObject();
+ writeComponent(collector);
}
}
}
diff --git a/src/qmltyperegistrar/qqmltypescreator_p.h b/src/qmltyperegistrar/qqmltypescreator_p.h
index 0d6dd91327..10c5a3d35d 100644
--- a/src/qmltyperegistrar/qqmltypescreator_p.h
+++ b/src/qmltyperegistrar/qqmltypescreator_p.h
@@ -37,6 +37,7 @@ public:
void setVersion(QTypeRevision version) { m_version = version; }
private:
+ void writeComponent(const QmlTypesClassDescription &collector);
void writeClassProperties(const QmlTypesClassDescription &collector);
void writeType(QAnyStringView type);
void writeProperties(const Property::Container &properties);
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index b4fd21fe7e..f892343b85 100644
--- a/src/qmlworkerscript/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -180,8 +180,8 @@ bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
} else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) {
QMutexLocker locker(&m_lock);
WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event);
- auto itr = workers.find(workerEvent->workerId());
- if (itr != workers.end()) {
+ auto itr = workers.constFind(workerEvent->workerId());
+ if (itr != workers.cend()) {
if (itr->isT1())
delete itr->asT1();
workers.erase(itr);
@@ -417,8 +417,8 @@ int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
void QQuickWorkerScriptEngine::removeWorkerScript(int id)
{
- const auto it = d->workers.find(id);
- if (it == d->workers.end())
+ const auto it = d->workers.constFind(id);
+ if (it == d->workers.cend())
return;
if (it->isT1()) {
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index f662bcbc2a..accf307382 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -461,13 +461,25 @@ void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDev
void QQuickTapHandler::connectPreRenderSignal(bool conn)
{
+ // disconnect pre-existing connection, if any
+ disconnect(m_preRenderSignalConnection);
+
auto par = parentItem();
- if (!par)
+ if (!par || !par->window())
return;
- if (conn)
- connect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
- else
- disconnect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+
+ /*
+ Note: beforeSynchronizing is emitted from the SG thread, and the
+ timeHeldChanged signal can be used to do arbitrary things in user QML.
+
+ But the docs say the GUI thread is blockd, and "Therefore, it is safe
+ to access GUI thread thread data in a slot or lambda that is connected
+ with Qt::DirectConnection." We use the default AutoConnection just in case.
+ */
+ if (conn) {
+ m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
+ this, &QQuickTapHandler::updateTimeHeld);
+ }
}
void QQuickTapHandler::updateTimeHeld()
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
index 95755b1110..a9908ebabd 100644
--- a/src/quick/handlers/qquicktaphandler_p.h
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -103,6 +103,7 @@ private:
QBasicTimer m_longPressTimer;
QBasicTimer m_doubleTapTimer;
QEventPoint m_singleTapReleasedPoint;
+ QMetaObject::Connection m_preRenderSignalConnection;
Qt::MouseButton m_singleTapReleasedButton;
int m_tapCount = 0;
int m_longPressThreshold = -1;
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 6ae5914970..d78bd040c2 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -1206,7 +1206,7 @@ QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
This signal is emitted when the \a region needs to be rendered. If a context
is active it can be referenced from the context property.
- This signal can be triggered by markdirty(), requestPaint() or by changing
+ This signal can be triggered by markDirty(), requestPaint() or by changing
the current canvas window.
*/
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 25ef44ecb0..e67d80b2d6 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1382,7 +1382,7 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
\qmlproperty int QtQuick::GridView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
*/
/*!
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 4c26708168..75037079fb 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2470,11 +2470,27 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
*/
bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
{
- QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward);
+ QQuickWindow *window = item->window();
+ const bool wrap = !window || window->isTopLevel();
+
+ QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap);
if (next == item)
return false;
+ if (!wrap && !next) {
+ // Focus chain wrapped and we are not top-level window
+ // Give focus to parent window
+ Q_ASSERT(window);
+ Q_ASSERT(window->parent());
+
+ qt_window_private(window->parent())->setFocusToTarget(
+ forward ? QWindowPrivate::FocusTarget::Next
+ : QWindowPrivate::FocusTarget::Prev);
+ window->parent()->requestActivate();
+ return true;
+ }
+
next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
return true;
@@ -2524,7 +2540,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star
return nullptr;
}
-QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward)
+QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap)
{
Q_ASSERT(item);
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward;
@@ -2618,6 +2634,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
}
current = parent;
} else if (hasChildren) {
+ if (!wrap && (forward || firstFromItem != from)) {
+ qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
+ << "Focus chain about to wrap.";
+ // If focus chain wraps, we should give the parent window
+ // a chance to get focus, so we should stop here
+ return nullptr;
+ }
+
// Wrap around after checking all items forward
if (forward) {
current = firstChild;
@@ -9289,12 +9313,12 @@ void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFil
bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
// if there's no exclusive grabber, look for passive grabbers during filtering
if (isFiltering && !pointGrabber) {
- auto pg = event->passiveGrabbers(p);
+ const auto pg = event->passiveGrabbers(p);
if (!pg.isEmpty()) {
// It seems unlikely to have multiple passive grabbers of one eventpoint with different grandparents.
// So hopefully if we start from one passive grabber and go up the parent chain from there,
// we will find any filtering parent items that exist.
- auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.first());
+ auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.constFirst());
if (handler)
pointGrabber = handler->parentItem();
}
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index c2e014b72d..bb238904ca 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -575,7 +575,7 @@ public:
static bool focusNextPrev(QQuickItem *item, bool forward);
static QQuickItem *nextTabChildItem(const QQuickItem *item, int start);
static QQuickItem *prevTabChildItem(const QQuickItem *item, int start);
- static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward);
+ static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap = true);
static bool canAcceptTabFocus(QQuickItem *item);
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 8bccc7f4cb..11b2642748 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -993,9 +993,8 @@ QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPriv
QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
{
if (animationTemplate && pathUpdater()) {
- QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
- animationTemplate->activeAnimations.find(pathUpdater()->target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(pathUpdater()->target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
}
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 3fde95e213..6b185b4e7c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2411,7 +2411,7 @@ QQuickListView::~QQuickListView()
/*!
\qmlproperty int QtQuick::ListView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
*/
/*!
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 9c83183eb7..a6edfc4754 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -1284,6 +1284,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
return me.isAccepted();
}
+ Q_UNUSED(source)
return false;
}
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index b30084474d..3b37dd975b 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -1773,9 +1773,7 @@ QQuickText::~QQuickText()
By default, no variable axes are set.
- \note In order to use variable axes on Windows, the application has to run with either the
- FreeType or DirectWrite font databases. See the documentation for
- QGuiApplication::QGuiApplication() for more information on how to select these technologies.
+ \note On Windows, variable axes are not supported if the optional GDI font backend is in use.
\sa QFont::setVariableAxis()
//! [qml-font-variable-axes]
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 6443b9ef70..7812cf107e 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -326,13 +326,15 @@ void QQuickTextDocumentPrivate::load()
setStatus(QQuickTextDocument::Status::Loading, {});
QByteArray data = file.readAll();
doc->setBaseUrl(resolvedUrl.adjusted(QUrl::RemoveFilename));
+#if QT_CONFIG(textmarkdownreader) || QT_CONFIG(texthtmlparser)
const bool plainText = editor->textFormat() == QQuickTextEdit::PlainText;
+#endif
#if QT_CONFIG(textmarkdownreader)
if (!plainText && isMarkdown) {
doc->setMarkdown(QString::fromUtf8(data));
} else
#endif
-#ifndef QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(texthtmlparser)
if (!plainText && isHtml) {
// If a user loads an HTML file, remember the encoding.
// If the user then calls save() later, the same encoding will be used.
@@ -411,7 +413,7 @@ void QQuickTextDocumentPrivate::writeTo(const QUrl &fileUrl)
raw = doc->toMarkdown().toUtf8();
break;
#endif
-#ifndef QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(texthtmlparser)
case Qt::RichText:
if (sameUrl && encoding) {
QStringEncoder enc(*encoding);
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index fd33fa86e2..c486fece40 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -660,10 +660,14 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
if (borderStyle == QTextFrameFormat::BorderStyle_None)
return;
- addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
- -frameFormat.rightMargin() - borderWidth,
- -frameFormat.bottomMargin() - borderWidth),
- borderWidth, borderStyle, borderBrush);
+ const auto collapsed = table->format().borderCollapse();
+
+ if (!collapsed) {
+ addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
+ -frameFormat.rightMargin() - borderWidth,
+ -frameFormat.bottomMargin() - borderWidth),
+ borderWidth, borderStyle, borderBrush);
+ }
if (table != nullptr) {
int rows = table->rows();
int columns = table->columns();
@@ -673,7 +677,7 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
QTextTableCell cell = table->cellAt(row, column);
QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell);
- addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth,
+ addBorder(cellRect.adjusted(-borderWidth, -borderWidth, collapsed ? -borderWidth : 0, collapsed ? -borderWidth : 0), borderWidth,
borderStyle, borderBrush);
}
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index b8c7878e04..09f938a473 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -1853,6 +1853,35 @@ void QQuickWindowPrivate::clearFocusObject()
da->clearFocusObject();
}
+void QQuickWindowPrivate::setFocusToTarget(FocusTarget target)
+{
+ QQuickItem *newFocusItem = nullptr;
+ if (contentItem) {
+ switch (target) {
+ case FocusTarget::First:
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, true);
+ break;
+ case FocusTarget::Last:
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, false);
+ break;
+ case FocusTarget::Next:
+ case FocusTarget::Prev: {
+ auto da = deliveryAgentPrivate();
+ Q_ASSERT(da);
+ QQuickItem *focusItem = da->focusTargetItem() ? da->focusTargetItem() : contentItem;
+ bool forward = (target == FocusTarget::Next);
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(focusItem, forward);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (newFocusItem)
+ newFocusItem->setFocus(true, Qt::ActiveWindowFocusReason);
+}
+
/*!
\qmlproperty list<QtObject> Window::data
\qmldefault
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 1b1b12de65..3a70fda3d2 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -146,6 +146,7 @@ public:
#endif
void clearFocusObject() override;
+ void setFocusToTarget(QWindowPrivate::FocusTarget) override;
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index c4a978dec8..e539880452 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -35,7 +35,15 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow
: QQuickWindow(dd, parent)
{
connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged);
- connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged);
+ connect(this, &QWindow::visibilityChanged, this, [&]{
+ Q_D(QQuickWindowQmlImpl);
+ // Update the window's actual visibility and turn off visibilityExplicitlySet,
+ // so that future applyWindowVisibility() calls do not apply both window state
+ // and visible state, unless setVisibility() is called again by the user.
+ d->visibility = QWindow::visibility();
+ d->visibilityExplicitlySet = false;
+ emit QQuickWindowQmlImpl::visibilityChanged(d->visibility);
+ });
connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
// We shadow the x and y properties, so that we can re-map them in case
@@ -110,6 +118,7 @@ void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
{
Q_D(QQuickWindowQmlImpl);
d->visibility = visibility;
+ d->visibilityExplicitlySet = true;
if (d->componentComplete)
applyWindowVisibility();
}
@@ -190,8 +199,8 @@ void QQuickWindowQmlImpl::applyWindowVisibility()
Q_ASSERT(d->componentComplete);
- const bool visible = d->visibility == AutomaticVisibility
- ? d->visible : d->visibility != Hidden;
+ const bool visible = d->visibilityExplicitlySet
+ ? d->visibility != Hidden : d->visible;
qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this;
@@ -234,20 +243,30 @@ void QQuickWindowQmlImpl::applyWindowVisibility()
}
}
- if (d->visibleExplicitlySet && ((d->visibility == Hidden && d->visible) ||
- (d->visibility > AutomaticVisibility && !d->visible))) {
+ if (d->visibleExplicitlySet && d->visibilityExplicitlySet &&
+ ((d->visibility == Hidden && d->visible) ||
+ (d->visibility > AutomaticVisibility && !d->visible))) {
// FIXME: Should we bail out in this case?
qmlWarning(this) << "Conflicting properties 'visible' and 'visibility'";
}
if (d->visibility == AutomaticVisibility) {
+ // We're either showing for the first time, with the default
+ // visibility of AutomaticVisibility, or the user has called
+ // setVisibility with AutomaticVisibility at some point, so
+ // apply both window state and visible.
if (QWindow::parent() || visualParent())
setWindowState(Qt::WindowNoState);
else
setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
QQuickWindow::setVisible(d->visible);
- } else {
+ } else if (d->visibilityExplicitlySet) {
+ // We're not AutomaticVisibility, but the user has requested
+ // an explicit visibility, so apply both window state and visible.
QQuickWindow::setVisibility(d->visibility);
+ } else {
+ // Our window state should be up to date, so only apply visible
+ QQuickWindow::setVisible(d->visible);
}
}
diff --git a/src/quick/items/qquickwindowmodule_p_p.h b/src/quick/items/qquickwindowmodule_p_p.h
index bda666e15b..227b8aa01e 100644
--- a/src/quick/items/qquickwindowmodule_p_p.h
+++ b/src/quick/items/qquickwindowmodule_p_p.h
@@ -30,6 +30,8 @@ public:
bool visible = false;
bool visibleExplicitlySet = false;
QQuickWindow::Visibility visibility = QQuickWindow::AutomaticVisibility;
+ bool visibilityExplicitlySet = false;
+
QV4::PersistentValue rootItemMarker;
QMetaObject::Connection itemParentWindowChangeListener;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
index 159f058422..a661e47765 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -208,8 +208,8 @@ void QSGMaterialShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
clearCachedRendererData();
for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) {
- auto it = shaderFileNames.find(stage);
- if (it != shaderFileNames.end()) {
+ auto it = shaderFileNames.constFind(stage);
+ if (it != shaderFileNames.cend()) {
QString fn = *it;
const QShader s = loadShader(*it);
if (!s.isValid())
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index e21f2fbb6f..8026f68eb4 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -103,7 +103,7 @@ QSGAdaptationBackendData *contextFactory()
// caused by run time hocus pocus. If one wants to use the software backend
// in a GL or Vulkan capable Qt build (or on Windows or Apple platforms), it
// has to be requested explicitly.
-#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !defined(Q_OS_WIN) && !defined(Q_OS_MACOS) && !defined(Q_OS_IOS)
+#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !QT_CONFIG(metal) && !defined(Q_OS_WIN)
if (requestedBackend.isEmpty())
requestedBackend = QLatin1String("software");
#endif
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index 8d80b13f56..c3b7b42cb9 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -702,7 +702,7 @@ QSGRhiShaderEffectDefaultShader QSGRhiShaderEffectDefaultShader::create(const QS
{
QSGRhiShaderEffectDefaultShader s;
s.shader = loadShaderFromFile(filename);
- const QList<QShaderDescription::BlockVariable> uboMembers = s.shader.description().uniformBlocks().first().members;
+ const QList<QShaderDescription::BlockVariable> uboMembers = s.shader.description().uniformBlocks().constFirst().members;
for (const auto &member: uboMembers) {
if (member.name == QByteArrayLiteral("qt_Matrix"))
s.matrixArrayByteSize = member.size;
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index a24a3e6160..45c183a5f8 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -1602,6 +1602,8 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormat(uint nativeFormat, QRhiTex
default:
return QRhiTexture::UnknownFormat;
}
+ Q_UNUSED(nativeFormat)
+ Q_UNUSED(flags)
}
bool QSGRhiSupport::attemptReinitWithSwRastUponFail() const
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 658a90182f..34032d4801 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -652,6 +652,27 @@ void QQuickDeliveryAgentPrivate::updateFocusItemTransform()
#endif
}
+/*!
+ Returns the item that should get active focus when the
+ root focus scope gets active focus.
+*/
+QQuickItem *QQuickDeliveryAgentPrivate::focusTargetItem() const
+{
+ if (activeFocusItem)
+ return activeFocusItem;
+
+ Q_ASSERT(rootItem);
+ QQuickItem *targetItem = rootItem;
+
+ while (targetItem->isFocusScope()
+ && targetItem->scopedFocusItem()
+ && targetItem->scopedFocusItem()->isEnabled()) {
+ targetItem = targetItem->scopedFocusItem();
+ }
+
+ return targetItem;
+}
+
/*! \internal
If called during event delivery, returns the agent that is delivering the
event, without checking whether \a item is reachable from there.
@@ -821,16 +842,7 @@ bool QQuickDeliveryAgent::event(QEvent *ev)
case QEvent::InputMethod:
case QEvent::InputMethodQuery:
{
- QQuickItem *target = d->activeFocusItem;
- // while an input method delivers the event, this window might still be inactive
- if (!target) {
- target = d->rootItem;
- if (!target || !target->isEnabled())
- break;
- // see setFocusInScope for a similar loop
- while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
- target = target->scopedFocusItem();
- }
+ QQuickItem *target = d->focusTargetItem();
if (target)
QCoreApplication::sendEvent(target, ev);
}
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index 20ba0036fa..5678afb9f7 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -67,6 +67,8 @@ public:
void clearFocusObject();
void updateFocusItemTransform();
+ QQuickItem *focusTargetItem() const;
+
// Keeps track of the item currently receiving mouse events
#if QT_CONFIG(quick_draganddrop)
QQuickDragGrabber *dragGrabber = nullptr;
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index 1ddf131a89..b19e6f03d9 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -52,14 +52,12 @@ QSmoothedAnimation::~QSmoothedAnimation()
delete delayedStopTimer;
if (animationTemplate) {
if (target.object()) {
- QHash<QQmlProperty, QSmoothedAnimation* >::iterator it =
- animationTemplate->activeAnimations.find(target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
} else {
//target is no longer valid, need to search linearly
- QHash<QQmlProperty, QSmoothedAnimation* >::iterator it;
- for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ for (auto it = animationTemplate->activeAnimations.cbegin(); it != animationTemplate->activeAnimations.cend(); ++it) {
if (it.value() == this) {
animationTemplate->activeAnimations.erase(it);
break;
diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp
index 678f1c4238..4b615d69b7 100644
--- a/src/quick/util/qquickspringanimation.cpp
+++ b/src/quick/util/qquickspringanimation.cpp
@@ -132,12 +132,12 @@ QSpringAnimation::~QSpringAnimation()
{
if (animationTemplate) {
if (target.object()) {
- ActiveAnimationHashIt it = animationTemplate->activeAnimations.find(target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
} else {
//target is no longer valid, need to search linearly
- for (ActiveAnimationHashIt it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ for (auto it = animationTemplate->activeAnimations.cbegin(); it != animationTemplate->activeAnimations.cend(); ++it) {
if (it.value() == this) {
animationTemplate->activeAnimations.erase(it);
break;
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 7d589cf95e..1f066270c6 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -419,27 +419,25 @@ void QQuickState::addEntryToRevertList(const QQuickStateAction &action)
void QQuickState::removeAllEntriesFromRevertList(QObject *target)
{
- Q_D(QQuickState);
-
- if (isStateActive()) {
- const auto actionMatchesTarget = [target](QQuickSimpleAction &simpleAction) {
- if (simpleAction.property().object() == target) {
- QQmlPropertyPrivate::removeBinding(simpleAction.property());
- simpleAction.property().write(simpleAction.value());
- if (auto binding = simpleAction.binding()) {
- QQmlProperty prop = simpleAction.property();
- binding.installOn(prop);
- }
-
- return true;
- }
- return false;
- };
-
- d->revertList.erase(std::remove_if(d->revertList.begin(), d->revertList.end(),
- actionMatchesTarget),
- d->revertList.end());
- }
+ Q_D(QQuickState);
+
+ if (isStateActive()) {
+ const auto actionMatchesTarget = [target](const QQuickSimpleAction &simpleAction) {
+ if (simpleAction.property().object() == target) {
+ QQmlPropertyPrivate::removeBinding(simpleAction.property());
+ simpleAction.property().write(simpleAction.value());
+ if (auto binding = simpleAction.binding()) {
+ QQmlProperty prop = simpleAction.property();
+ binding.installOn(prop);
+ }
+
+ return true;
+ }
+ return false;
+ };
+
+ d->revertList.removeIf(actionMatchesTarget);
+ }
}
void QQuickState::addEntriesToRevertList(const QList<QQuickStateAction> &actionList)
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index efaa01c305..700e06a8ba 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -190,8 +190,8 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
return false;
};
- auto newEnd = std::remove_if(applyList.begin(), applyList.end(), isHandledInTransition);
- applyList.erase(newEnd, applyList.end());
+
+ applyList.removeIf(isHandledInTransition);
}
// Any actions remaining have not been handled by the transition and should
diff --git a/src/quickcontrols/material/Popup.qml b/src/quickcontrols/material/Popup.qml
index c161f23054..e443a1c2ff 100644
--- a/src/quickcontrols/material/Popup.qml
+++ b/src/quickcontrols/material/Popup.qml
@@ -39,7 +39,7 @@ T.Popup {
layer.enabled: control.Material.elevation > 0
layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
- roundedScale: control.background.radius
+ roundedScale: control.Material.roundedScale
}
}
diff --git a/src/quickcontrols/qquickattachedpropertypropagator.cpp b/src/quickcontrols/qquickattachedpropertypropagator.cpp
index 6447bf84a4..cf752a4248 100644
--- a/src/quickcontrols/qquickattachedpropertypropagator.cpp
+++ b/src/quickcontrols/qquickattachedpropertypropagator.cpp
@@ -44,6 +44,9 @@ Q_LOGGING_CATEGORY(lcAttached, "qt.quick.controls.attachedpropertypropagator")
\li Call \l initialize() in the constructor
\li Define set/inherit/propagate/reset functions for each property as needed
\li Reimplement \l attachedParentChange() to handle property inheritance
+ \li Implement a static \c qmlAttachedProperties function and declare the
+ type as an attached QML type with \l QML_ELEMENT and \l QML_ATTACHED,
+ as detailed in \l {Providing Attached Properties}
\endlist
For an example that demonstrates this in depth, see
diff --git a/src/quickcontrols/qquickstyle.cpp b/src/quickcontrols/qquickstyle.cpp
index 347a2ab179..a40e9535e2 100644
--- a/src/quickcontrols/qquickstyle.cpp
+++ b/src/quickcontrols/qquickstyle.cpp
@@ -306,6 +306,7 @@ QSharedPointer<QSettings> QQuickStylePrivate::settings(const QString &group)
return QSharedPointer<QSettings>(settings);
}
#endif // QT_NO_SETTINGS
+ Q_UNUSED(group)
return QSharedPointer<QSettings>();
}
diff --git a/src/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp
index 2fa3c7e820..41f45259ea 100644
--- a/src/quicklayouts/qquicklinearlayout.cpp
+++ b/src/quicklayouts/qquicklinearlayout.cpp
@@ -441,7 +441,7 @@ void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item)
void QQuickGridLayoutBase::rearrange(const QSizeF &size)
{
Q_D(QQuickGridLayoutBase);
- if (!isReady())
+ if (!isReady() || !size.isValid())
return;
qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index f5db64bcfb..6aa88a465e 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -625,6 +625,8 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState == ExitTransition && transitionManager.isRunning())
return false;
+ Q_ASSERT(popupItem);
+
// We need to cache the original scale and opacity values so we can reset it after
// the exit transition is done so they have the original values again
prevScale = popupItem->scale();
@@ -634,24 +636,8 @@ bool QQuickPopupPrivate::prepareExitTransition()
// The setFocus(false) call below removes any active focus before we're
// able to check it in finalizeExitTransition.
if (!hadActiveFocusBeforeExitTransition) {
- const auto hasFocusInRoot = [](QQuickItem *item) {
- Q_ASSERT(item);
- if (!item->window() || item->window()->isActive())
- return item->hasActiveFocus();
-
- // fallback for when there's no active window
- const auto *da = QQuickItemPrivate::get(item)->deliveryAgentPrivate();
- if (!da || !da->rootItem)
- return false;
-
- QQuickItem *focusItem = da->rootItem;
- while (focusItem->isFocusScope() && focusItem->scopedFocusItem())
- focusItem = focusItem->scopedFocusItem();
-
- return focusItem == item;
- };
-
- hadActiveFocusBeforeExitTransition = hasFocusInRoot(popupItem);
+ const auto *da = QQuickItemPrivate::get(popupItem)->deliveryAgentPrivate();
+ hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus() || (da && da->focusTargetItem() == popupItem);
}
if (focus)
diff --git a/src/quickvectorimage/generator/qquickgenerator.cpp b/src/quickvectorimage/generator/qquickgenerator.cpp
index 4a71dd7e73..505b10f919 100644
--- a/src/quickvectorimage/generator/qquickgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickgenerator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickgenerator_p.h"
#include "qsvgvisitorimpl_p.h"
diff --git a/src/quickvectorimage/generator/qquickgenerator_p.h b/src/quickvectorimage/generator/qquickgenerator_p.h
index 99ed089073..c951ad800c 100644
--- a/src/quickvectorimage/generator/qquickgenerator_p.h
+++ b/src/quickvectorimage/generator/qquickgenerator_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKGENERATOR_P_H
#define QQUICKGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qquickitemgenerator.cpp b/src/quickvectorimage/generator/qquickitemgenerator.cpp
index 1917c53013..171cb6889f 100644
--- a/src/quickvectorimage/generator/qquickitemgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickitemgenerator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickitemgenerator_p.h"
#include "utils_p.h"
@@ -27,7 +27,6 @@ QQuickItemGenerator::QQuickItemGenerator(const QString fileName, QQuickVectorIma
QQuickItemGenerator::~QQuickItemGenerator()
{
-
}
void QQuickItemGenerator::generateNodeBase(const NodeInfo &info)
@@ -107,7 +106,6 @@ void QQuickItemGenerator::generatePath(const PathNodeInfo &info)
m_parentShapeItem = shapeItem;
m_inShapeItem = true;
- // Check ??
generateNodeBase(info);
optimizePaths(info);
@@ -123,7 +121,7 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
Q_UNUSED(pathSelector)
Q_ASSERT(painterPath || quadPath);
- const bool noPen = info.strokeColor == QColorConstants::Transparent;
+ const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
return;
@@ -137,20 +135,24 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
QQuickShapePath *shapePath = new QQuickShapePath;
Q_ASSERT(shapePath);
- if (!info.nodeId.isEmpty()) {
-
+ if (!info.nodeId.isEmpty())
shapePath->setObjectName(QStringLiteral("svg_path:") + info.nodeId);
- }
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
shapePath->setStrokeColor(Qt::transparent);
} else {
- shapePath->setStrokeColor(info.strokeColor);
- shapePath->setStrokeWidth(info.strokeWidth);
+ shapePath->setStrokeColor(info.strokeStyle.color);
+ shapePath->setStrokeWidth(info.strokeStyle.width);
+ shapePath->setCapStyle(QQuickShapePath::CapStyle(info.strokeStyle.lineCapStyle));
+ shapePath->setJoinStyle(QQuickShapePath::JoinStyle(info.strokeStyle.lineJoinStyle));
+ shapePath->setMiterLimit(info.strokeStyle.miterLimit);
+ if (info.strokeStyle.dashArray.length() != 0) {
+ shapePath->setStrokeStyle(QQuickShapePath::DashLine);
+ shapePath->setDashPattern(info.strokeStyle.dashArray.toVector());
+ shapePath->setDashOffset(info.strokeStyle.dashOffset);
+ }
}
- shapePath->setCapStyle(QQuickShapePath::CapStyle(info.capStyle));
-
if (!(pathSelector & QQuickVectorImageGenerator::FillPath))
shapePath->setFillColor(Qt::transparent);
else if (info.grad.type() != QGradient::NoGradient)
@@ -195,27 +197,25 @@ void QQuickItemGenerator::generateGradient(const QGradient *grad, QQuickShapePat
QRectF gradRect(linGrad->start(), linGrad->finalStop());
QRectF logRect = linGrad->coordinateMode() == QGradient::LogicalMode ? gradRect : QQuickVectorImageGenerator::Utils::mapToQtLogicalMode(gradRect, boundingRect);
- auto *quickGrad = new QQuickShapeLinearGradient(shapePath);
-
- quickGrad->setX1(logRect.left());
- quickGrad->setY1(logRect.top());
- quickGrad->setX2(logRect.right());
- quickGrad->setY2(logRect.bottom());
- setStops(quickGrad, linGrad->stops());
+ auto *quickGrad = new QQuickShapeLinearGradient(shapePath);
+ quickGrad->setX1(logRect.left());
+ quickGrad->setY1(logRect.top());
+ quickGrad->setX2(logRect.right());
+ quickGrad->setY2(logRect.bottom());
+ setStops(quickGrad, linGrad->stops());
- shapePath->setFillGradient(quickGrad);
+ shapePath->setFillGradient(quickGrad);
} else if (grad->type() == QGradient::RadialGradient) {
auto *radGrad = static_cast<const QRadialGradient*>(grad);
-
- auto *quickGrad = new QQuickShapeRadialGradient(shapePath);
- quickGrad->setCenterX(radGrad->center().x());
- quickGrad->setCenterY(radGrad->center().y());
- quickGrad->setCenterRadius(radGrad->radius());
- quickGrad->setFocalX(radGrad->focalPoint().x());
- quickGrad->setFocalY(radGrad->focalPoint().y());
- setStops(quickGrad, radGrad->stops());
-
- shapePath->setFillGradient(quickGrad);
+ auto *quickGrad = new QQuickShapeRadialGradient(shapePath);
+ quickGrad->setCenterX(radGrad->center().x());
+ quickGrad->setCenterY(radGrad->center().y());
+ quickGrad->setCenterRadius(radGrad->radius());
+ quickGrad->setFocalX(radGrad->focalPoint().x());
+ quickGrad->setFocalY(radGrad->focalPoint().y());
+ setStops(quickGrad, radGrad->stops());
+
+ shapePath->setFillGradient(quickGrad);
}
}
@@ -225,8 +225,8 @@ void QQuickItemGenerator::generateNode(const NodeInfo &info)
return;
qCWarning(lcQuickVectorImage) << "SVG NODE NOT IMPLEMENTED: "
- << info.nodeId
- << " type: " << info.typeName;
+ << info.nodeId
+ << " type: " << info.typeName;
}
void QQuickItemGenerator::generateTextNode(const TextNodeInfo &info)
diff --git a/src/quickvectorimage/generator/qquickitemgenerator_p.h b/src/quickvectorimage/generator/qquickitemgenerator_p.h
index 2b8eda46ea..e96812ec30 100644
--- a/src/quickvectorimage/generator/qquickitemgenerator_p.h
+++ b/src/quickvectorimage/generator/qquickitemgenerator_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKITEMGENERATOR_P_H
#define QQUICKITEMGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qquicknodeinfo_p.h b/src/quickvectorimage/generator/qquicknodeinfo_p.h
index 28629bf3e1..b7123f53e0 100644
--- a/src/quickvectorimage/generator/qquicknodeinfo_p.h
+++ b/src/quickvectorimage/generator/qquicknodeinfo_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKNODEINFO_P_H
#define QQUICKNODEINFO_P_H
@@ -44,14 +44,23 @@ struct ImageNodeInfo : NodeInfo
QRectF rect;
};
+struct StrokeStyle
+{
+ Qt::PenCapStyle lineCapStyle = Qt::SquareCap;
+ Qt::PenJoinStyle lineJoinStyle = Qt::MiterJoin;
+ qreal miterLimit = 4;
+ qreal dashOffset = 0;
+ QList<qreal> dashArray;
+ QColor color = QColorConstants::Transparent;
+ qreal width = 1.0;
+};
+
struct PathNodeInfo : NodeInfo
{
QPainterPath painterPath;
Qt::FillRule fillRule = Qt::FillRule::WindingFill;
- Qt::PenCapStyle capStyle = Qt::SquareCap;
- QColor strokeColor;
- qreal strokeWidth;
QColor fillColor;
+ StrokeStyle strokeStyle;
QGradient grad;
};
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator.cpp b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
index 731d80f2da..7db7983e68 100644
--- a/src/quickvectorimage/generator/qquickqmlgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickqmlgenerator_p.h"
#include "qquicknodeinfo_p.h"
@@ -246,7 +246,7 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
Q_UNUSED(pathSelector)
Q_ASSERT(painterPath || quadPath);
- const bool noPen = info.strokeColor == QColorConstants::Transparent;
+ const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
return;
@@ -275,11 +275,17 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
stream() << "strokeColor: \"transparent\"";
} else {
- stream() << "strokeColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
- stream() << "strokeWidth: " << info.strokeWidth;
+ stream() << "strokeColor: \"" << info.strokeStyle.color.name(QColor::HexArgb) << "\"";
+ stream() << "strokeWidth: " << info.strokeStyle.width;
+ stream() << "capStyle: " << QQuickVectorImageGenerator::Utils::strokeCapStyleString(info.strokeStyle.lineCapStyle);
+ stream() << "joinStyle: " << QQuickVectorImageGenerator::Utils::strokeJoinStyleString(info.strokeStyle.lineJoinStyle);
+ stream() << "miterLimit: " << info.strokeStyle.miterLimit;
+ if (info.strokeStyle.dashArray.length() != 0) {
+ stream() << "strokeStyle: " << "ShapePath.DashLine";
+ stream() << "dashPattern: " << QQuickVectorImageGenerator::Utils::listString(info.strokeStyle.dashArray);
+ stream() << "dashOffset: " << info.strokeStyle.dashOffset;
+ }
}
- if (info.capStyle == Qt::FlatCap)
- stream() << "capStyle: ShapePath.FlatCap"; //### TODO Add the rest of the styles, as well as join styles etc.
if (!(pathSelector & QQuickVectorImageGenerator::FillPath)) {
stream() << "fillColor: \"transparent\"";
@@ -460,11 +466,12 @@ bool QQuickQmlGenerator::generateRootNode(const StructureNodeInfo &info)
const QStringList comments = m_commentString.split(u'\n');
if (!isNodeVisible(info)) {
- if (comments.isEmpty())
+ if (comments.isEmpty()) {
stream() << "// Generated from SVG";
- else
+ } else {
for (const auto &comment : comments)
stream() << "// " << comment;
+ }
stream() << "import QtQuick";
stream() << "import QtQuick.Shapes" << Qt::endl;
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator_p.h b/src/quickvectorimage/generator/qquickqmlgenerator_p.h
index 5b52497a97..81fc4d386b 100644
--- a/src/quickvectorimage/generator/qquickqmlgenerator_p.h
+++ b/src/quickvectorimage/generator/qquickqmlgenerator_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKQMLGENERATOR_P_H
#define QQUICKQMLGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl.cpp b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
index ce595f9a6b..f91e35ffaa 100644
--- a/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsvgvisitorimpl_p.h"
#include "qquickgenerator_p.h"
@@ -34,52 +34,6 @@ using namespace Qt::StringLiterals;
Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
-static inline bool isPathContainer(const QSvgStructureNode *node)
-{
- bool foundPath = false;
- for (const auto *child : node->renderers()) {
- switch (child->type()) {
- // nodes that shouldn't go inside Shape{}
- case QSvgNode::Switch:
- case QSvgNode::Doc:
- case QSvgNode::Group:
- case QSvgNode::Animation:
- case QSvgNode::Use:
- case QSvgNode::Video:
- //qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
- return false;
-
- // nodes that could go inside Shape{}
- case QSvgNode::Defs:
- case QSvgNode::Image:
- case QSvgNode::Textarea:
- case QSvgNode::Text:
- case QSvgNode::Tspan:
- break;
-
- // nodes that are done as pure ShapePath{}
- case QSvgNode::Rect:
- case QSvgNode::Circle:
- case QSvgNode::Ellipse:
- case QSvgNode::Line:
- case QSvgNode::Path:
- case QSvgNode::Polygon:
- case QSvgNode::Polyline:
- if (!child->style().transform.isDefault()) {
- //qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
- return false;
- }
- foundPath = true;
- break;
- default:
- qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
- break;
- }
- }
- //qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
- return foundPath;
-}
-
class QSvgStyleResolver
{
public:
@@ -141,17 +95,6 @@ public:
return strokeColor;
}
- qreal currentStrokeOpacity() const
- {
- return m_svgState.strokeOpacity;
- }
-
- float currentStrokeWidth() const
- {
- float penWidth = m_dummyPainter.pen().widthF();
- return penWidth ? penWidth : 1;
- }
-
static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
{
QGradient grad = gradient;
@@ -166,20 +109,90 @@ public:
return grad;
}
+ float currentStrokeWidth() const
+ {
+ float penWidth = m_dummyPainter.pen().widthF();
+ return penWidth ? penWidth : 1;
+ }
+
+ QPen currentStroke() const
+ {
+ return m_dummyPainter.pen();
+ }
+
protected:
QPainter m_dummyPainter;
QImage m_dummyImage;
QSvgExtraStates m_svgState;
-
};
Q_GLOBAL_STATIC(QSvgStyleResolver, styleResolver)
+namespace {
+inline bool isPathContainer(const QSvgStructureNode *node)
+{
+ bool foundPath = false;
+ for (const auto *child : node->renderers()) {
+ switch (child->type()) {
+ // nodes that shouldn't go inside Shape{}
+ case QSvgNode::Switch:
+ case QSvgNode::Doc:
+ case QSvgNode::Group:
+ case QSvgNode::Animation:
+ case QSvgNode::Use:
+ case QSvgNode::Video:
+ //qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
+ return false;
+
+ // nodes that could go inside Shape{}
+ case QSvgNode::Defs:
+ case QSvgNode::Image:
+ case QSvgNode::Textarea:
+ case QSvgNode::Text:
+ case QSvgNode::Tspan:
+ break;
+
+ // nodes that are done as pure ShapePath{}
+ case QSvgNode::Rect:
+ case QSvgNode::Circle:
+ case QSvgNode::Ellipse:
+ case QSvgNode::Line:
+ case QSvgNode::Path:
+ case QSvgNode::Polygon:
+ case QSvgNode::Polyline:
+ if (!child->style().transform.isDefault()) {
+ //qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
+ return false;
+ }
+ foundPath = true;
+ break;
+ default:
+ qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
+ break;
+ }
+ }
+ //qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
+ return foundPath;
+}
+
+void populateStrokeStyle(StrokeStyle &srokeStyle)
+{
+ QPen p = styleResolver->currentStroke();
+ srokeStyle.lineCapStyle = p.capStyle();
+ srokeStyle.lineJoinStyle = p.joinStyle() == Qt::SvgMiterJoin ? Qt::MiterJoin : p.joinStyle(); //TODO support SvgMiterJoin
+ srokeStyle.miterLimit = p.miterLimit();
+ srokeStyle.dashOffset = p.dashOffset();
+ srokeStyle.dashArray = p.dashPattern();
+ srokeStyle.color = styleResolver->currentStrokeColor();
+ srokeStyle.width = p.widthF();
+}
+
+};
+
QSvgVisitorImpl::QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator)
: m_svgFileName(svgFileName)
, m_generator(generator)
{
-
}
void QSvgVisitorImpl::traverse()
@@ -202,7 +215,7 @@ void QSvgVisitorImpl::visitNode(const QSvgNode *node)
{
handleBaseNodeSetup(node);
- ImageNodeInfo info;
+ NodeInfo info;
fillCommonNodeInfo(node, info);
m_generator->generateNode(info);
@@ -254,10 +267,7 @@ void QSvgVisitorImpl::visitRectNode(const QSvgRect *node)
p.lineTo(x1, y1 + ry);
p.arcTo(x1, y1, rx * 2, ry * 2, 180, -90); // ARC to x1 + rx, y1
-
handlePathNode(node, p);
-
- return;
}
void QSvgVisitorImpl::visitEllipseNode(const QSvgEllipse *node)
@@ -277,11 +287,10 @@ void QSvgVisitorImpl::visitPathNode(const QSvgPath *node)
void QSvgVisitorImpl::visitLineNode(const QSvgLine *node)
{
- // TODO: proper end caps (should be flat by default?)
QPainterPath p;
p.moveTo(node->line().p1());
p.lineTo(node->line().p2());
- handlePathNode(node, p, Qt::FlatCap);
+ handlePathNode(node, p);
}
void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
@@ -293,7 +302,7 @@ void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
void QSvgVisitorImpl::visitPolylineNode(const QSvgPolyline *node)
{
QPainterPath p = QQuickVectorImageGenerator::Utils::polygonToPath(node->polygon(), false);
- handlePathNode(node, p, Qt::FlatCap);
+ handlePathNode(node, p);
}
QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
@@ -367,7 +376,9 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
bool needsRichText = false;
bool preserveWhiteSpace = node->whitespaceMode() == QSvgText::Preserve;
const QGradient *mainGradient = styleResolver->currentFillGradient();
+#if QT_CONFIG(texthtmlparser)
bool needsPathNode = mainGradient != nullptr;
+#endif
for (const auto *tspan : node->tspans()) {
if (!tspan) {
text += QStringLiteral("<br>");
@@ -406,14 +417,18 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
&& styleResolver->currentFillGradient() != mainGradient) {
const QGradient grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
styleTagContent += gradientCssDescription(&grad) + u';';
+#if QT_CONFIG(texthtmlparser)
needsPathNode = true;
+#endif
}
QString strokeColor = colorCssDescription(styleResolver->currentStrokeColor());
if (!strokeColor.isEmpty()) {
styleTagContent += QStringLiteral("-qt-stroke-color:%1;").arg(strokeColor);
styleTagContent += QStringLiteral("-qt-stroke-width:%1;").arg(styleResolver->currentStrokeWidth());
+#if QT_CONFIG(texthtmlparser)
needsPathNode = true;
+#endif
}
if (tspan->whitespaceMode() == QSvgText::Preserve && !preserveWhiteSpace)
@@ -442,7 +457,6 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
}
}
-
needsRichText = needsRichText || !styleTagContent.isEmpty();
if (!styleTagContent.isEmpty())
text += QStringLiteral("<span style=\"%1\">").arg(styleTagContent);
@@ -494,7 +508,7 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
if (font.pixelSize() <= 0 && font.pointSize() > 0)
font.setPixelSize(font.pointSize()); // Pixel size stored as point size by SVG parser
-#ifndef QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(texthtmlparser)
if (needsPathNode) {
QTextDocument document;
document.setHtml(text);
@@ -526,11 +540,11 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
info.painterPath = p;
if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
- info.strokeWidth = fmt.textOutline().widthF();
- info.strokeColor = fmt.textOutline().color();
+ info.strokeStyle.width = fmt.textOutline().widthF();
+ info.strokeStyle.color = fmt.textOutline().color();
} else {
- info.strokeColor = styleResolver->currentStrokeColor();
- info.strokeWidth = styleResolver->currentStrokeWidth();
+ info.strokeStyle.color = styleResolver->currentStrokeColor();
+ info.strokeStyle.width = styleResolver->currentStrokeWidth();
}
if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
@@ -735,10 +749,9 @@ void QSvgVisitorImpl::handleBaseNodeEnd(const QSvgNode *node)
qCDebug(lcQuickVectorImage) << "After END" << node << "fill" << styleResolver->currentFillColor()
<< "stroke" << styleResolver->currentStrokeColor() << styleResolver->currentStrokeWidth()
<< node->nodeId();
-
}
-void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path, Qt::PenCapStyle capStyle)
+void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path)
{
handleBaseNodeSetup(node);
@@ -749,10 +762,8 @@ void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &p
info.fillRule = fillStyle->fillRule();
info.painterPath = path;
- info.capStyle = capStyle;
info.fillColor = styleResolver->currentFillColor();
- info.strokeColor = styleResolver->currentStrokeColor();
- info.strokeWidth = styleResolver->currentStrokeWidth();
+ populateStrokeStyle(info.strokeStyle);
if (styleResolver->currentFillGradient() != nullptr)
info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl_p.h b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
index d80c9ec992..af8a8d386e 100644
--- a/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSVGVISITORIMPL_P_H
#define QSVGVISITORIMPL_P_H
@@ -54,7 +54,7 @@ private:
void handleBaseNodeSetup(const QSvgNode *node);
void handleBaseNode(const QSvgNode *node);
void handleBaseNodeEnd(const QSvgNode *node);
- void handlePathNode(const QSvgNode *node, const QPainterPath &path, Qt::PenCapStyle capStyle = Qt::SquareCap);
+ void handlePathNode(const QSvgNode *node, const QPainterPath &path);
void outputShapePath(QPainterPath pathCopy, const PathNodeInfo &info);
static QString gradientCssDescription(const QGradient *gradient);
static QString colorCssDescription(QColor color);
diff --git a/src/quickvectorimage/generator/utils_p.h b/src/quickvectorimage/generator/utils_p.h
index 4d0b7e3ab4..bb65ee2b69 100644
--- a/src/quickvectorimage/generator/utils_p.h
+++ b/src/quickvectorimage/generator/utils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef UTILS_P_H
#define UTILS_P_H
@@ -193,6 +193,70 @@ inline QString toSvgString(const QQuadPath &path)
return svgPathString;
}
+inline QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle)
+{
+ QString capStyle;
+ switch (strokeCapStyle) {
+ case Qt::FlatCap:
+ capStyle = QStringLiteral("ShapePath.FlatCap");
+ break;
+ case Qt::SquareCap:
+ capStyle = QStringLiteral("ShapePath.SquareCap");
+ break;
+ case Qt::RoundCap:
+ capStyle = QStringLiteral("ShapePath.RoundCap");
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return capStyle;
+}
+
+inline QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle)
+{
+ QString joinStyle;
+ switch (strokeJoinStyle) {
+ case Qt::MiterJoin:
+ joinStyle = QStringLiteral("ShapePath.MiterJoin");
+ break;
+ case Qt::BevelJoin:
+ joinStyle = QStringLiteral("ShapePath.BevelJoin");
+ break;
+ case Qt::RoundJoin:
+ joinStyle = QStringLiteral("ShapePath.RoundJoin");
+ break;
+ default:
+ //TODO: Add support for SvgMiter case
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return joinStyle;
+}
+
+template<typename T>
+inline QString listString(QList<T> list)
+{
+ if (list.isEmpty())
+ return QStringLiteral("[]");
+
+ QString listString;
+ QTextStream stream(&listString);
+ stream << "[";
+
+ if (list.length() > 1) {
+ for (int i = 0; i < list.length() - 1; i++) {
+ T v = list[i];
+ stream << v << ", ";
+ }
+ }
+
+ stream << list.last() << "]";
+ return listString;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/quickvectorimage/qquickvectorimage.cpp b/src/quickvectorimage/qquickvectorimage.cpp
index d2039c5d38..813679b853 100644
--- a/src/quickvectorimage/qquickvectorimage.cpp
+++ b/src/quickvectorimage/qquickvectorimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickvectorimage_p.h"
#include "qquickvectorimage_p_p.h"
diff --git a/src/quickvectorimage/qquickvectorimage_p.h b/src/quickvectorimage/qquickvectorimage_p.h
index d2aa3c51cc..68ccc561a1 100644
--- a/src/quickvectorimage/qquickvectorimage_p.h
+++ b/src/quickvectorimage/qquickvectorimage_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKVECTORIMAGE_P_H
#define QQUICKVECTORIMAGE_P_H
diff --git a/src/quickvectorimage/qquickvectorimage_p_p.h b/src/quickvectorimage/qquickvectorimage_p_p.h
index b0cd4b83d3..689a5e9b69 100644
--- a/src/quickvectorimage/qquickvectorimage_p_p.h
+++ b/src/quickvectorimage/qquickvectorimage_p_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKVECTORIMAGE_P_P_H
#define QQUICKVECTORIMAGE_P_P_H
diff --git a/src/quickvectorimage/qquickvectorimageglobal_p.h b/src/quickvectorimage/qquickvectorimageglobal_p.h
index 83b96642c1..d6c636bc84 100644
--- a/src/quickvectorimage/qquickvectorimageglobal_p.h
+++ b/src/quickvectorimage/qquickvectorimageglobal_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTQUICKVECTORIMAGEGLOBAL_P_H
#define QTQUICKVECTORIMAGEGLOBAL_P_H
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 16893b77e4..0e168cf6ad 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -155,7 +155,4 @@ if(TARGET Qt::Quick)
)
endif()
endif()
-
- add_subdirectory(qmltc_build_failures)
endif()
-
diff --git a/tests/auto/cmake/qmltc_build_failures/CMakeLists.txt b/tests/auto/cmake/qmltc_build_failures/CMakeLists.txt
deleted file mode 100644
index 25d62b807d..0000000000
--- a/tests/auto/cmake/qmltc_build_failures/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(qmltc_build_failures)
-
-_qt_internal_test_expect_build_fail(nontoplevelrequiredproperty)
diff --git a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/CMakeLists.txt b/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/CMakeLists.txt
deleted file mode 100644
index 1e28a9b817..0000000000
--- a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-
-project(nontoplevelrequiredproperty)
-
-find_package(Qt6 REQUIRED Quick REQUIRED ${Qt6Tests_PREFIX_PATH})
-
-qt_standard_project_setup()
-
-qt_add_executable(executable
- main.cpp
-)
-
-qt6_add_qml_module(executable
- VERSION 1.0
- URI QmltcBuildFailures
- QML_FILES
- Main.qml
- DEPENDENCIES
- QtQuick
- ENABLE_TYPE_COMPILER
-)
-
-target_link_libraries(executable PRIVATE Qt6::Quick)
diff --git a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/main.cpp b/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/main.cpp
deleted file mode 100644
index 2cd0d051b6..0000000000
--- a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/main.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "main.h"
-
-int main() { }
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt
index d841a1beb5..f0fa09a66a 100644
--- a/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt
+++ b/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt
@@ -14,6 +14,7 @@ target_link_libraries(tst_generate_qmlls_ini PRIVATE Qt6::Test)
set(QT_QML_GENERATE_QMLLS_INI ON CACHE BOOL "" FORCE)
add_subdirectory(SomeSubfolder)
+add_subdirectory(Dotted)
qt_add_qml_module(tst_generate_qmlls_ini
URI MainModule
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt
new file mode 100644
index 0000000000..e3d4518aae
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(Uri)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt
new file mode 100644
index 0000000000..a2edbc25ce
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_qml_module(DottedUri
+ URI Dotted.Uri
+ QML_FILES Main.qml
+)
+
+add_subdirectory(Hello)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt
new file mode 100644
index 0000000000..ecceec7e73
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(World)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt
new file mode 100644
index 0000000000..338674bc93
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_qml_module(DottedUri2
+ URI Dotted.Uri.Hello.World
+ QML_FILES Main.qml
+)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml
new file mode 100644
index 0000000000..f97cbcf115
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml
@@ -0,0 +1,4 @@
+import QtQuick
+
+Item {
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml
new file mode 100644
index 0000000000..f97cbcf115
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml
@@ -0,0 +1,4 @@
+import QtQuick
+
+Item {
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/main.cpp b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp
index 33cf424a2d..a7bdbf1e18 100644
--- a/tests/auto/cmake/test_generate_qmlls_ini/main.cpp
+++ b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp
@@ -45,6 +45,7 @@ void tst_generate_qmlls_ini::qmllsIniAreCorrect()
secondFolder.absolutePath()));
}
+ {
QDir sourceSubfolder = source;
QVERIFY(sourceSubfolder.cd(u"SomeSubfolder"_s));
QDir buildSubfolder(build.absolutePath().append(u"/SomeSubfolder/qml/Some/Sub/Folder"_s));
@@ -56,6 +57,38 @@ void tst_generate_qmlls_ini::qmllsIniAreCorrect()
QCOMPARE(fileContent,
u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(buildSubfolder.absolutePath()));
}
+ }
+
+ {
+ QDir dottedUriSubfolder = source;
+ QVERIFY(dottedUriSubfolder.cd(u"Dotted"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Uri"_s));
+ {
+ auto file = QFile(dottedUriSubfolder.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ QCOMPARE(
+ fileContent,
+ u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(build.absolutePath()));
+ }
+ }
+ {
+ QDir dottedUriSubfolder = source;
+ QVERIFY(dottedUriSubfolder.cd(u"Dotted"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Uri"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Hello"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"World"_s));
+ {
+ auto file = QFile(dottedUriSubfolder.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ QCOMPARE(
+ fileContent,
+ u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(build.absolutePath()));
+ }
+ }
}
QTEST_MAIN(tst_generate_qmlls_ini)
diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt
index fefa6ff1c1..8a5e039673 100644
--- a/tests/auto/core/CMakeLists.txt
+++ b/tests/auto/core/CMakeLists.txt
@@ -3,4 +3,6 @@
add_subdirectory(qqmlstandardpaths)
add_subdirectory(qqmlsysteminformation)
-add_subdirectory(qqmlsettings)
+if(QT_FEATURE_settings)
+ add_subdirectory(qqmlsettings)
+endif()
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 6302df235d..6b81f4c616 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -34,7 +34,9 @@ add_subdirectory(qqmlerror)
add_subdirectory(qqmlincubator)
add_subdirectory(qqmlinfo)
add_subdirectory(qqmllistreference)
-add_subdirectory(qqmllocale)
+if(QT_FEATURE_qml_locale)
+ add_subdirectory(qqmllocale)
+endif()
add_subdirectory(qqmlmetaobject)
if(NOT ANDROID) # QTBUG-100003
add_subdirectory(qqmlmoduleplugin)
@@ -46,7 +48,9 @@ add_subdirectory(qqmlpromise)
add_subdirectory(qtqmlmodules)
add_subdirectory(qquickfolderlistmodel)
add_subdirectory(qqmlapplicationengine)
-add_subdirectory(qqmlsettings)
+if(QT_FEATURE_settings)
+ add_subdirectory(qqmlsettings)
+endif()
if(NOT INTEGRITY)
# There's no mounted filesystem on INTEGRITY therefore skipping qmldiskcache
diff --git a/tests/auto/qml/ecmascripttests/test262.py b/tests/auto/qml/ecmascripttests/test262.py
deleted file mode 100755
index e701d32966..0000000000
--- a/tests/auto/qml/ecmascripttests/test262.py
+++ /dev/null
@@ -1,611 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2017 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-# Copyright 2009 the Sputnik authors. All rights reserved.
-# This code is governed by the BSD license found in the LICENSE file.
-
-# This is derived from sputnik.py, the Sputnik console test runner,
-# with elements from packager.py, which is separately
-# copyrighted. TODO: Refactor so there is less duplication between
-# test262.py and packager.py.
-
-import sys
-from os import path
-rootDir = path.dirname(path.realpath(__file__))
-sys.path.insert(0, path.abspath(rootDir + "/test262/tools/packaging"))
-
-import logging
-import optparse
-import os
-import platform
-import re
-import subprocess
-import tempfile
-import time
-import xml.dom.minidom
-import datetime
-import shutil
-import json
-import stat
-import multiprocessing
-import signal
-
-
-from parseTestRecord import parseTestRecord, stripHeader
-
-from packagerConfig import *
-
-# excluded features that are still experimental and not part of any official standard
-# see also the features.txt file in test262/
-excludedFeatures = [
- "BigInt",
- "class-fields-public",
- "class-fields-private",
- "Promise.prototype.finally",
- "async-iteration",
- "Symbol.asyncIterator",
- "object-rest",
- "object-spread",
- "optional-catch-binding",
- "regexp-dotall",
- "regexp-lookbehind",
- "regexp-named-groups",
- "regexp-unicode-property-escapes",
- "Atomics",
- "SharedArrayBuffer",
- "Array.prototype.flatten",
- "Array.prototype.flatMap",
- "string-trimming",
- "String.prototype.trimEnd",
- "String.prototype.trimStart",
- "numeric-separator-literal",
-
- # optional features, not supported by us
- "caller"
-]
-
-# ############# Helpers needed for parallel multi-process test execution ############
-
-def runTest(case, args):
- return case.Run(args)
-
-def runTestVarArgs(args):
- return runTest(*args)
-
-def initWorkerProcess():
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-# #############
-
-class Test262Error(Exception):
- def __init__(self, message):
- self.message = message
-
-def ReportError(s):
- raise Test262Error(s)
-
-
-class TestExpectations:
- def __init__(self, enabled):
- self.testsToSkip = []
- self.failingTests = []
- f = open(rootDir + "/TestExpectations")
- if not enabled:
- return
- for line in f.read().splitlines():
- line = line.strip()
- if len(line) == 0 or line[0] == "#":
- continue
- record = line.split()
- if len(record) == 1:
- self.failingTests.append(record[0])
- else:
- test = record[0]
- expectation = record[1]
- if expectation == "skip":
- self.testsToSkip.append(test)
- f.close()
-
- def update(self, progress):
- unexpectedPasses = [c.case.name for c in progress.failed_tests if c.case.IsNegative()]
-
- # If a test fails that we expected to fail, then it actually passed unexpectedly.
- failures = [c.case.name for c in progress.failed_tests if not c.case.IsNegative()]
- for failure in failures:
- if failure in self.failingTests:
- unexpectedPasses.append(failure)
-
- f = open(rootDir + "/TestExpectations")
- lines = f.read().splitlines()
- oldLen = len(lines)
- for result in unexpectedPasses:
- expectationLine = result
- try:
- lines.remove(expectationLine)
- except ValueError:
- pass
-
- f.close()
- if len(lines) != oldLen:
- f = open(rootDir + "/TestExpectations", "w")
- f.write("\n".join(lines))
- f.close()
- print "Changes to TestExpectations written!"
-
-
-if not os.path.exists(EXCLUDED_FILENAME):
- print "Cannot generate (JSON) test262 tests without a file," + \
- " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME
- sys.exit(1)
-EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME)
-EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test")
-EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST]
-
-
-def BuildOptions():
- result = optparse.OptionParser()
- result.add_option("--command", default="qmljs", help="The command-line to run")
- result.add_option("--tests", default=path.abspath(rootDir + '/test262'),
- help="Path to the tests")
- result.add_option("--cat", default=False, action="store_true",
- help="Print packaged test code that would be run")
- result.add_option("--summary", default=True, action="store_true",
- help="Print summary after running tests")
- result.add_option("--full-summary", default=False, action="store_true",
- help="Print summary and test output after running tests")
- result.add_option("--strict_only", default=False, action="store_true",
- help="Test only strict mode")
- result.add_option("--non_strict_only", default=False, action="store_true",
- help="Test only non-strict mode")
- result.add_option("--parallel", default=False, action="store_true",
- help="Run tests in parallel")
- result.add_option("--with-test-expectations", default=False, action="store_true",
- help="Parse TestExpectations to deal with tests known to fail")
- result.add_option("--update-expectations", default=False, action="store_true",
- help="Update test expectations fail when a test passes that was expected to fail")
- # TODO: Once enough tests are made strict compat, change the default
- # to "both"
- result.add_option("--unmarked_default", default="non_strict",
- help="default mode for tests of unspecified strictness")
- return result
-
-
-def ValidateOptions(options):
- if not options.command:
- ReportError("A --command must be specified.")
- if not path.exists(options.tests):
- ReportError("Couldn't find test path '%s'" % options.tests)
-
-
-placeHolderPattern = re.compile(r"\{\{(\w+)\}\}")
-
-
-def IsWindows():
- p = platform.system()
- return (p == 'Windows') or (p == 'Microsoft')
-
-
-class TempFile(object):
-
- def __init__(self, suffix="", prefix="tmp", text=False):
- self.suffix = suffix
- self.prefix = prefix
- self.text = text
- self.fd = None
- self.name = None
- self.is_closed = False
- self.Open()
-
- def Open(self):
- (self.fd, self.name) = tempfile.mkstemp(
- suffix = self.suffix,
- prefix = self.prefix,
- text = self.text)
-
- def Write(self, str):
- os.write(self.fd, str)
-
- def Read(self):
- f = file(self.name)
- result = f.read()
- f.close()
- return result
-
- def Close(self):
- if not self.is_closed:
- self.is_closed = True
- os.close(self.fd)
-
- def Dispose(self):
- try:
- self.Close()
- os.unlink(self.name)
- except OSError, e:
- logging.error("Error disposing temp file: %s", str(e))
-
-
-class TestResult(object):
-
- def __init__(self, exit_code, stdout, stderr, case):
- self.exit_code = exit_code
- self.stdout = stdout
- self.stderr = stderr
- self.case = case
-
- def ReportOutcome(self, long_format):
- name = self.case.GetName()
- mode = self.case.GetMode()
- if self.HasUnexpectedOutcome():
- if self.case.IsNegative():
- print "=== %s was expected to fail in %s, but didn't ===" % (name, mode)
- else:
- if long_format:
- print "=== %s failed in %s ===" % (name, mode)
- else:
- print "%s in %s: " % (name, mode)
- out = self.stdout.strip()
- if len(out) > 0:
- print "--- output ---"
- print out
- err = self.stderr.strip()
- if len(err) > 0:
- print "--- errors ---"
- print err
- if long_format:
- print "==="
- elif self.case.IsNegative():
- print "%s failed in %s as expected" % (name, mode)
- else:
- print "%s passed in %s" % (name, mode)
-
- def HasFailed(self):
- return self.exit_code != 0
-
- def HasUnexpectedOutcome(self):
- if self.case.IsNegative():
- return not self.HasFailed()
- else:
- return self.HasFailed()
-
-
-class TestCase(object):
-
- def __init__(self, suite, name, full_path, strict_mode):
- self.suite = suite
- self.name = name
- self.full_path = full_path
- self.strict_mode = strict_mode
- f = open(self.full_path)
- self.contents = f.read()
- f.close()
- testRecord = parseTestRecord(self.contents, name)
- self.test = testRecord["test"]
- if 'features' in testRecord:
- self.features = testRecord["features"];
- else:
- self.features = []
- del testRecord["test"]
- del testRecord["header"]
- self.testRecord = testRecord;
-
-
- def GetName(self):
- return self.name
-
- def GetMode(self):
- if self.strict_mode:
- return "strict mode"
- else:
- return "non-strict mode"
-
- def GetPath(self):
- return self.name
-
- def NegateResult(self):
- if self.IsNegative():
- del self.testRecord['negative']
- else:
- self.testRecord['negative'] = "Some failure";
-
- def IsNegative(self):
- return 'negative' in self.testRecord
-
- def IsOnlyStrict(self):
- return 'onlyStrict' in self.testRecord
-
- def IsNoStrict(self):
- return 'noStrict' in self.testRecord
-
- def IsExperimental(self):
- for f in self.features:
- if excludedFeatures.count(f) >= 1:
- return True;
- return False
-
- def GetSource(self):
- # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \
- source = self.suite.GetInclude("assert.js") + \
- self.suite.GetInclude("sta.js") + \
- self.test + '\n'
- if 'includes' in self.testRecord:
- for inc in self.testRecord['includes']:
- source += self.suite.GetInclude(inc);
-
- if self.strict_mode:
- source = '"use strict";\nvar strict_mode = true;\n' + source
- else:
- source = "var strict_mode = false; \n" + source
- return source
-
- def InstantiateTemplate(self, template, params):
- def GetParameter(match):
- key = match.group(1)
- return params.get(key, match.group(0))
- return placeHolderPattern.sub(GetParameter, template)
-
- def Execute(self, command):
- if IsWindows():
- args = '%s' % command
- else:
- args = command.split(" ")
- stdout = TempFile(prefix="test262-out-")
- stderr = TempFile(prefix="test262-err-")
- try:
- logging.info("exec: %s", str(args))
- process = subprocess.Popen(
- args,
- shell = IsWindows(),
- stdout = stdout.fd,
- stderr = stderr.fd
- )
- code = process.wait()
- out = stdout.Read()
- err = stderr.Read()
- finally:
- stdout.Dispose()
- stderr.Dispose()
- return (code, out, err)
-
- def RunTestIn(self, command_template, tmp):
- tmp.Write(self.GetSource())
- tmp.Close()
- command = self.InstantiateTemplate(command_template, {
- 'path': tmp.name
- })
- (code, out, err) = self.Execute(command)
- return TestResult(code, out, err, self)
-
- def Run(self, command_template):
- tmp = TempFile(suffix=".js", prefix="test262-", text=True)
- try:
- result = self.RunTestIn(command_template, tmp)
- finally:
- tmp.Dispose()
- return result
-
- def Print(self):
- print self.GetSource()
-
-
-class ProgressIndicator(object):
-
- def __init__(self, count):
- self.count = count
- self.succeeded = 0
- self.failed = 0
- self.failed_tests = []
-
- def HasRun(self, result):
- result.ReportOutcome(True)
- if result.HasUnexpectedOutcome():
- self.failed += 1
- self.failed_tests.append(result)
- else:
- self.succeeded += 1
-
-
-def MakePlural(n):
- if (n == 1):
- return (n, "")
- else:
- return (n, "s")
-
-
-class TestSuite(object):
-
- def __init__(self, root, strict_only, non_strict_only, unmarked_default, load_expectations):
- # TODO: derive from packagerConfig.py
- self.test_root = path.join(root, 'test')
- self.lib_root = path.join(root, 'harness')
- self.strict_only = strict_only
- self.non_strict_only = non_strict_only
- self.unmarked_default = unmarked_default
- self.include_cache = { }
- self.expectations = TestExpectations(load_expectations)
-
- def IsExcludedTest(self, path):
- if path.startswith('annexB'):
- return True;
- if path.startswith('harness'):
- return True;
- if path.startswith('intl402'):
- return True;
- return False;
-
- def Validate(self):
- if not path.exists(self.test_root):
- ReportError("No test repository found")
- if not path.exists(self.lib_root):
- ReportError("No test library found")
-
- def IsHidden(self, path):
- return path.startswith('.') or path == 'CVS'
-
- def IsTestCase(self, path):
- return path.endswith('.js')
-
- def ShouldRun(self, rel_path, tests):
- if len(tests) == 0:
- return True
- for test in tests:
- if test in rel_path:
- return True
- return False
-
- def GetInclude(self, name):
- if not name in self.include_cache:
- static = path.join(self.lib_root, name)
- if path.exists(static):
- f = open(static)
- contents = stripHeader(f.read())
- contents = re.sub(r'\r\n', '\n', contents)
- self.include_cache[name] = contents + "\n"
- f.close()
- else:
- ReportError("Can't find: " + static)
- return self.include_cache[name]
-
- def EnumerateTests(self, tests):
- logging.info("Listing tests in %s", self.test_root)
- cases = []
- for root, dirs, files in os.walk(self.test_root):
- for f in [x for x in dirs if self.IsHidden(x)]:
- dirs.remove(f)
- dirs.sort()
- for f in sorted(files):
- if self.IsTestCase(f):
- full_path = path.join(root, f)
- if full_path.startswith(self.test_root):
- rel_path = full_path[len(self.test_root)+1:]
- else:
- logging.warning("Unexpected path %s", full_path)
- rel_path = full_path
- if self.ShouldRun(rel_path, tests) and not self.IsExcludedTest(rel_path):
- basename = path.basename(full_path)[:-3]
- name = rel_path.replace('.js', '')
- if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(name) >= 1:
- print 'Excluded: ' + rel_path
- else:
- if not self.non_strict_only:
- strict_case = TestCase(self, name, full_path, True)
- if self.expectations.failingTests.count(name) >= 1:
- strict_case.NegateResult()
- if not strict_case.IsNoStrict() and not strict_case.IsExperimental():
- if strict_case.IsOnlyStrict() or \
- self.unmarked_default in ['both', 'strict']:
- cases.append(strict_case)
- if not self.strict_only:
- non_strict_case = TestCase(self, name, full_path, False)
- if self.expectations.failingTests.count(name) >= 1:
- non_strict_case.NegateResult()
- if not non_strict_case.IsOnlyStrict() and not non_strict_case.IsExperimental():
- if non_strict_case.IsNoStrict() or \
- self.unmarked_default in ['both', 'non_strict']:
- cases.append(non_strict_case)
- logging.info("Done listing tests")
- return cases
-
- def PrintSummary(self, progress):
- print
- print "=== Summary ==="
- count = progress.count
- succeeded = progress.succeeded
- failed = progress.failed
- print " - Ran %i test%s" % MakePlural(count)
- if progress.failed == 0:
- print " - All tests succeeded"
- else:
- percent = ((100.0 * succeeded) / count,)
- print " - Passed %i test%s (%.1f%%)" % (MakePlural(succeeded) + percent)
- percent = ((100.0 * failed) / count,)
- print " - Failed %i test%s (%.1f%%)" % (MakePlural(failed) + percent)
- positive = [c for c in progress.failed_tests if not c.case.IsNegative()]
- negative = [c for c in progress.failed_tests if c.case.IsNegative()]
- if len(positive) > 0:
- print
- print "Failed tests"
- for result in positive:
- print " %s in %s" % (result.case.GetName(), result.case.GetMode())
- if len(negative) > 0:
- print
- print "Expected to fail but passed ---"
- for result in negative:
- print " %s in %s" % (result.case.GetName(), result.case.GetMode())
-
- def PrintFailureOutput(self, progress):
- for result in progress.failed_tests:
- print
- result.ReportOutcome(False)
-
- def Run(self, command_template, tests, print_summary, full_summary, parallel, update_expectations):
- if not "{{path}}" in command_template:
- command_template += " {{path}}"
- cases = self.EnumerateTests(tests)
- if len(cases) == 0:
- ReportError("No tests to run")
- progress = ProgressIndicator(len(cases))
-
- if parallel:
- pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=initWorkerProcess)
- results = pool.imap_unordered(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=multiprocessing.cpu_count() * 8)
- for result in results:
- progress.HasRun(result)
- else:
- for case in cases:
- result = case.Run(command_template)
- progress.HasRun(result)
- if print_summary:
- self.PrintSummary(progress)
- if full_summary:
- self.PrintFailureOutput(progress)
- else:
- print
- print "Use --full-summary to see output from failed tests"
- print
- if update_expectations:
- self.expectations.update(progress)
- return progress.failed == 0
-
- def Print(self, tests):
- cases = self.EnumerateTests(tests)
- if len(cases) > 0:
- cases[0].Print()
-
-
-def Main():
- # Uncomment the next line for more logging info.
- #logging.basicConfig(level=logging.DEBUG)
- # Some date tests rely on being run in pacific time and the USA's locale:
- os.environ["TZ"] = "America/Los_Angeles" # it *matters* that this is (7m8s) *East* of PST's nominal meridian !
- os.environ["LANG"] = "en_US.UTF-8"
- os.environ["LC_TIME"] = "en_US.UTF-8"
- parser = BuildOptions()
- (options, args) = parser.parse_args()
- ValidateOptions(options)
- test_suite = TestSuite(options.tests,
- options.strict_only,
- options.non_strict_only,
- options.unmarked_default,
- options.with_test_expectations)
- test_suite.Validate()
- if options.cat:
- test_suite.Print(args)
- return 0
- else:
- if test_suite.Run(options.command, args,
- options.summary or options.full_summary,
- options.full_summary,
- options.parallel,
- options.update_expectations):
- return 0
- else:
- return 1
-
-
-if __name__ == '__main__':
- try:
- sys.exit(Main())
- except Test262Error, e:
- print "Error: %s" % e.message
- sys.exit(1)
diff --git a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
index 76fa1328e7..450560833f 100644
--- a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
+++ b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
@@ -145,9 +145,11 @@ void tst_qjsonbinding::cppJsConversion()
{
QJSValue jsValue = eng.toScriptValue(jsonValue);
+#if QT_DEPRECATED_SINCE(6, 9)
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QVERIFY(!jsValue.isVariant());
QT_WARNING_POP
+#endif
switch (jsonValue.type()) {
case QJsonValue::Null:
QVERIFY(jsValue.isNull());
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 4d9b6aea41..8c5449d192 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -125,6 +125,7 @@ set(qml_files
dialog.qml
dialogButtonBox.qml
dynamicscene.qml
+ enforceSignature.qml
enumConversion.qml
enumFromBadSingleton.qml
enumInvalid.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
index 2b6cc09c19..8dd640c67f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
@@ -52,7 +52,6 @@ private:
};
struct Foozle {
- Q_GADGET
int foo = 1;
};
diff --git a/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
new file mode 100644
index 0000000000..571a000199
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+
+ function arg(item: Binding) : QtObject { return item }
+ function ret(item: QtObject) : Binding { return item }
+
+ property QtObject a: arg(mainItem);
+ property QtObject b: ret(mainItem);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index ae8ef49b22..9b66143f62 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -89,6 +89,7 @@ private slots:
void enumProblems();
void enumScope();
void enums();
+ void enforceSignature();
void enumsInOtherObject();
void equalityQObjects();
void equalityQUrl();
@@ -1652,6 +1653,23 @@ void tst_QmlCppCodegen::enums()
}
+void tst_QmlCppCodegen::enforceSignature()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enforceSignature.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const QVariant a = object->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(a.value<QObject *>(), nullptr);
+
+ const QVariant b = object->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(b.value<QObject *>(), nullptr);
+}
+
void tst_QmlCppCodegen::enumsInOtherObject()
{
QQmlEngine engine;
@@ -1865,9 +1883,8 @@ void tst_QmlCppCodegen::failures()
{
const auto &aotFailure
= QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
- QVERIFY(aotFailure.argumentTypes.isEmpty());
QVERIFY(!aotFailure.functionPtr);
- QCOMPARE(aotFailure.extraData, 0);
+ QCOMPARE(aotFailure.functionIndex, 0);
}
void tst_QmlCppCodegen::fallbackLookups()
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 0bf19bbe9f..da3ebc69a2 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -509,9 +509,23 @@ void TestQmlformat::testExample_data()
QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
+ QStringList exampleFiles;
+ QStringList testFiles;
QStringList files;
- files << findFiles(QDir(examples));
- files << findFiles(QDir(tests));
+ exampleFiles << findFiles(QDir(examples));
+ testFiles << findFiles(QDir(tests));
+
+ // Actually this test is an e2e test and not the unit test.
+ // At the moment of writing, CI lacks providing instruments for the automated tests
+ // which might be time-consuming, as for example this one.
+ // Therefore as part of QTBUG-122990 this test was copied to the /manual/e2e/qml/qmlformat
+ // however very small fraction of the test data is still preserved here for the sake of
+ // testing automatically at least a small part of the examples
+ const int nBatch = 10;
+ files << exampleFiles.mid(0, nBatch) << exampleFiles.mid(exampleFiles.size() / 2, nBatch)
+ << exampleFiles.mid(exampleFiles.size() - nBatch, nBatch);
+ files << testFiles.mid(0, nBatch) << testFiles.mid(exampleFiles.size() / 2, nBatch)
+ << testFiles.mid(exampleFiles.size() - nBatch, nBatch);
for (const QString &file : files)
QTest::newRow(qPrintable(file)) << file;
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml
new file mode 100644
index 0000000000..de142337b4
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml
@@ -0,0 +1,9 @@
+import QtQml
+import QtQuick
+
+QtObject {
+ id: root
+ component Comp : Item { }
+ property Comp c: Comp{ }
+ function comp() { return root.c }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml
new file mode 100644
index 0000000000..0585ceceb2
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+QtObject {
+ function enumeration() { return Text.AlignRight }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml
new file mode 100644
index 0000000000..dc03311e73
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ function f() { }
+ function method() { return f }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml
new file mode 100644
index 0000000000..bb79978d85
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ id: root
+ property int i: 1
+ function prop() { return root.i }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml
new file mode 100644
index 0000000000..78f02a8b67
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ function type() { return 1 + 1 }
+}
diff --git a/tests/auto/qml/qmllint/lintplugin.cpp b/tests/auto/qml/qmllint/lintplugin.cpp
index 58b174cb6b..65795c103c 100644
--- a/tests/auto/qml/qmllint/lintplugin.cpp
+++ b/tests/auto/qml/qmllint/lintplugin.cpp
@@ -23,7 +23,7 @@ public:
void run(const QQmlSA::Element &element) override
{
auto property = element.property(u"radius"_s);
- if (!property.isValid() || element.property(u"radius"_s).typeName() != u"double") {
+ if (!property.isValid() || element.property(u"radius"_s).typeName() != u"qreal") {
emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation());
return;
}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 4e69fc2e9e..57cb7228d8 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1026,7 +1026,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
{ Message { QStringLiteral("Ready") } } } };
QTest::newRow("nullBinding") << QStringLiteral("nullBinding.qml")
<< Result{ { Message{ QStringLiteral(
- "Cannot assign literal of type null to double") } } };
+ "Cannot assign literal of type null to qreal") } } };
QTest::newRow("missingRequiredAlias")
<< QStringLiteral("missingRequiredAlias.qml")
<< Result { { Message {
@@ -1368,10 +1368,11 @@ void TestQmllint::compilerWarnings_data()
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("shadowable")
+ << QStringLiteral("shadowable.qml")
+ << Result { { Message {QStringLiteral(
+ "with type NotSoSimple (stored as QQuickItem) can be shadowed") } } }
+ << true;
QTest::newRow("tooFewParameters")
<< QStringLiteral("tooFewParams.qml")
<< Result { { Message { QStringLiteral("No matching override found") } } } << true;
@@ -1402,6 +1403,39 @@ void TestQmllint::compilerWarnings_data()
QStringLiteral("Cannot retrieve a non-object type by ID: stateMachine")
} } }
<< true;
+ QTest::newRow("returnTypeAnnotation-component")
+ << QStringLiteral("returnTypeAnnotation_component.qml")
+ << Result{ { { "Could not compile function comp: function without return type "
+ "annotation returns (component in" },
+ { "returnTypeAnnotation_component.qml)::c with type Comp (stored as "
+ "QQuickItem). This may prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-enum")
+ << QStringLiteral("returnTypeAnnotation_enum.qml")
+ << Result{ { { "Could not compile function enumeration: function without return type "
+ "annotation returns QQuickText::HAlignment::AlignRight (stored as int). "
+ "This may prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-method")
+ << QStringLiteral("returnTypeAnnotation_method.qml")
+ << Result{ { { "Could not compile function method: function without return type "
+ "annotation returns (component in " }, // Don't check the build folder path
+ { "returnTypeAnnotation_method.qml)::f(...) (stored as QJSValue). This may "
+ "prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-property")
+ << QStringLiteral("returnTypeAnnotation_property.qml")
+ << Result{ { { "Could not compile function prop: function without return type "
+ "annotation returns (component in " }, // Don't check the build folder path
+ { "returnTypeAnnotation_property.qml)::i with type int. This may prevent "
+ "proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-type")
+ << QStringLiteral("returnTypeAnnotation_type.qml")
+ << Result{ { { "Could not compile function type: function without return type "
+ "annotation returns double. This may prevent proper compilation to "
+ "Cpp." } } }
+ << true;
}
void TestQmllint::compilerWarnings()
diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
index 55266c80d9..85c01d0a5f 100644
--- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
@@ -27,6 +27,7 @@ qt6_add_qml_module(tst_qmltc_qprocess
URI QmltcQProcessTests
SOURCES
cpptypes/testtype.h
+ cpptypes/typewithrequiredproperty.h
DEPENDENCIES
QtQuick/auto
QML_FILES
@@ -43,6 +44,10 @@ qt6_add_qml_module(tst_qmltc_qprocess
data/QmlBaseFromAnotherModule.qml
data/invalidTypeAnnotation.qml
data/constructFromString.qml
+ data/unboundRequiredPropertyInInlineComponent.qml
+ data/componentDefinitionInnerRequiredProperty.qml
+ data/componentDefinitionInnerRequiredPropertyFromOutside.qml
+ data/innerLevelRequiredProperty.qml
)
set(common_libraries
diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h
new file mode 100644
index 0000000000..1b0c69efaa
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TYPEWITHREQUIREDPROPERTY_H_
+#define TYPEWITHREQUIREDPROPERTY_H_
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqmlregistration.h>
+
+class TypeWithRequiredProperty : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(QString requiredProperty READ requiredProperty WRITE setRequiredProperty REQUIRED)
+
+ QProperty<QString> m_requiredProperty;
+
+public:
+ TypeWithRequiredProperty(QObject *parent = nullptr) : QObject(parent) { }
+
+ QString requiredProperty() const { return m_requiredProperty; }
+ void setRequiredProperty(const QString &s) { m_requiredProperty = s; }
+};
+
+#endif // TYPEWITHREQUIREDPROPERTY_H_
diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml
new file mode 100644
index 0000000000..638cf9fa1e
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Component {
+ id: mycomp
+
+ Item {
+ Rectangle {
+ // Inner required properties cannot be set so this
+ // should produce an error
+ required property bool bar
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml
new file mode 100644
index 0000000000..56f00edbe9
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QmltcQProcessTests
+
+Item {
+ Component {
+ id: mycomp
+
+ Item {
+ // This introduces an inner required property
+ // without a binding that cannot be set later and should
+ // thus block the compilation.
+ TypeWithRequiredProperty {}
+ }
+ }
+}
diff --git a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/Main.qml b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml
index 8b28418670..8b28418670 100644
--- a/tests/auto/cmake/qmltc_build_failures/nontoplevelrequiredproperty/Main.qml
+++ b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml
diff --git a/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml
new file mode 100644
index 0000000000..3955a228d8
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ component InlineComponent: Item { required property int foo }
+
+ InlineComponent {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index fdb64f4a29..31c27c3cd7 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -55,6 +55,10 @@ private slots:
void qmlBaseFromAnotherModule();
void invalidTypeAnnotation();
void constructFromString();
+ void unboundRequiredPropertyInInlineComponent();
+ void componentDefinitionInnerRequiredProperty();
+ void componentDefinitionInnerRequiredPropertyFromOutside();
+ void innerLevelRequiredProperty();
};
#ifndef TST_QMLTC_QPROCESS_RESOURCES
@@ -340,5 +344,46 @@ void tst_qmltc_qprocess::constructFromString()
QVERIFY(errors.contains(warningMessage.arg(6).arg(23).arg(u"QSizeF")));
}
+void tst_qmltc_qprocess::unboundRequiredPropertyInInlineComponent()
+{
+ {
+ const auto errors = runQmltc(u"unboundRequiredPropertyInInlineComponent.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"unboundRequiredPropertyInInlineComponent.qml:9:5: Component is missing required property foo from InlineComponent [required]"_s
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::componentDefinitionInnerRequiredProperty()
+{
+ {
+ const auto errors = runQmltc(u"componentDefinitionInnerRequiredProperty.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"componentDefinitionInnerRequiredProperty.qml:11:13: Component is missing required property bar from here [required]"
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::componentDefinitionInnerRequiredPropertyFromOutside()
+{
+ {
+ const auto errors =
+ runQmltc(u"componentDefinitionInnerRequiredPropertyFromOutside.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"componentDefinitionInnerRequiredPropertyFromOutside.qml:15:13: Component is missing required property requiredProperty from TypeWithRequiredProperty [required]"
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::innerLevelRequiredProperty()
+{
+ {
+ const auto errors = runQmltc(u"innerLevelRequiredProperty.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"innerLevelRequiredProperty.qml:7:5: Component is missing required property foo from here [required]"
+ ));
+ }
+}
+
QTEST_MAIN(tst_qmltc_qprocess)
#include "tst_qmltc_qprocess.moc"
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
index 5334225692..68223ae6a5 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
@@ -10,7 +10,8 @@
qt_internal_add_cmake_library(foreign
STATIC
SOURCES
- foreign.cpp foreign.h foreign_p.h
+ foreign.cpp foreign.h
+ private/foreign_p.h
PUBLIC_LIBRARIES
Qt::Core
)
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h
index 6245dad796..ed23d78284 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h
+++ b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h
@@ -7,7 +7,6 @@
#include <QtCore/qobject.h>
// qmltyperegistrar will assume this file is reachable under <private/foreign_p.h>
-// It's not true, but this is how it works on actual private headers in Qt.
// See the trick in tst_qmltyperegistrar's CMakeLists.txt to turn on the --private-includes option.
class ForeignPrivate : public QObject
{
diff --git a/tests/auto/qml/qmltyperegistrar/missingTypes.json b/tests/auto/qml/qmltyperegistrar/missingTypes.json
index 3b57ae3c55..dacec11c4c 100644
--- a/tests/auto/qml/qmltyperegistrar/missingTypes.json
+++ b/tests/auto/qml/qmltyperegistrar/missingTypes.json
@@ -5,6 +5,25 @@
"classInfos": [
{
"name": "QML.Element",
+ "value": "int"
+ },
+ {
+ "name": "QML.Extended",
+ "value": "QQmlIntForeign"
+ },
+ {
+ "name": "QML.Foreign",
+ "value": "int"
+ }
+ ],
+ "className": "QQmlIntForeign",
+ "gadget": true,
+ "qualifiedClassName": "QQmlIntForeign"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
"value": "auto"
}
],
@@ -28,6 +47,20 @@
"write": "setPalette"
}
],
+ "enums": [
+ {
+ "isClass": false,
+ "isFlag": false,
+ "name": "RestorationMode",
+ "type": "NotAnUnderlyingType",
+ "values": [
+ "RestoreNone",
+ "RestoreBinding",
+ "RestoreValue",
+ "RestoreBindingOrValue"
+ ]
+ }
+ ],
"qualifiedClassName": "ExcessiveVersion",
"signals": [
{
@@ -69,22 +102,20 @@
"revision": 260,
"scriptable": true,
"stored": true,
- "type": "int",
+ "type": "NotAPropertyType",
"user": false
- },
+ }
+ ],
+ "methods": [
{
- "constant": true,
- "designable": true,
- "final": false,
- "index": 1,
- "name": "insane",
- "read": "revisioned",
- "required": false,
- "revision": 65297,
- "scriptable": true,
- "stored": true,
- "type": "int",
- "user": false
+ "access": "public",
+ "arguments": [
+ {
+ "type": "NotAnArgumentType"
+ }
+ ],
+ "name": "createAThing",
+ "returnType": "NotAReturnType"
}
],
"qualifiedClassName": "AddedInLateVersion",
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 563c88f850..822caea0d0 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -441,11 +441,22 @@ void tst_qmltyperegistrar::consistencyWarnings()
QTest::ignoreMessage(QtWarningMsg, message);
};
- expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used but cannot be found.");
- expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: "
+ "NotQObject is used as base type but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used as base type "
+ "but cannot be found.");
expectWarning("Warning: tst_qmltyperegistrar.h:: Invisible is declared as foreign type, "
"but cannot be found.");
- expectWarning("Warning: tst_qmltyperegistrar.h:: NotQByteArray is used but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQByteArray is used as sequence value type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAPropertyType is used as property type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnArgumentType is used as argument type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAReturnType is used as return type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnUnderlyingType is used as enum type "
+ "but cannot be found.");
processor.postProcessForeignTypes();
@@ -728,47 +739,47 @@ void tst_qmltyperegistrar::typedEnum()
exportMetaObjectRevisions: [256]
Enum {
name: "UChar"
- type: "quint8"
+ type: "uchar"
values: ["V0"]
}
Enum {
name: "Int8_T"
- type: "qint8"
+ type: "int8_t"
values: ["V1"]
}
Enum {
name: "UInt8_T"
- type: "quint8"
+ type: "uint8_t"
values: ["V2"]
}
Enum {
name: "Int16_T"
- type: "short"
+ type: "int16_t"
values: ["V3"]
}
Enum {
name: "UInt16_T"
- type: "ushort"
+ type: "uint16_t"
values: ["V4"]
}
Enum {
name: "Int32_T"
- type: "int"
+ type: "int32_t"
values: ["V5"]
}
Enum {
name: "UInt32_T"
- type: "uint"
+ type: "uint32_t"
values: ["V6"]
}
Enum {
name: "S"
- type: "short"
+ type: "qint16"
values: ["A", "B", "C"]
}
Enum {
name: "T"
- type: "ushort"
+ type: "quint16"
values: ["D", "E", "F"]
}
Enum {
@@ -794,7 +805,7 @@ void tst_qmltyperegistrar::listSignal()
prototype: "QObject"
Signal {
name: "objectListHappened"
- Parameter { type: "QObjectList" }
+ Parameter { type: "QList<QObject*>" }
}
})"));
}
@@ -982,10 +993,10 @@ void tst_qmltyperegistrar::longNumberTypes()
exports: ["QmlTypeRegistrarTest/LongNumberTypes 1.0"]
isCreatable: true
exportMetaObjectRevisions: [256]
- Property { name: "a"; type: "qlonglong"; index: 0 }
- Property { name: "b"; type: "qlonglong"; index: 1 }
- Property { name: "c"; type: "qulonglong"; index: 2 }
- Property { name: "d"; type: "qulonglong"; index: 3 }
+ Property { name: "a"; type: "qint64"; index: 0 }
+ Property { name: "b"; type: "int64_t"; index: 1 }
+ Property { name: "c"; type: "quint64"; index: 2 }
+ Property { name: "d"; type: "uint64_t"; index: 3 }
})"));
}
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index 5ebb9b6796..371fb840d1 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -5,7 +5,7 @@
#define TST_QMLTYPEREGISTRAR_H
#include "foreign.h"
-#include "foreign_p.h"
+#include "private/foreign_p.h"
#include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h>
diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt
index 7951baedb8..9745f31bdb 100644
--- a/tests/auto/qml/qqmlengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlengine/CMakeLists.txt
@@ -40,6 +40,7 @@ qt_add_qml_module(tst_qqmlengine_qml
SOURCES
"declarativelyregistered.h"
"declarativelyregistered.cpp"
+ "variantlistQJsonConversion.h"
RESOURCE_PREFIX
"/"
OUTPUT_DIRECTORY
diff --git a/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml
new file mode 100644
index 0000000000..fd0820a3c5
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml
@@ -0,0 +1,18 @@
+import QtQuick
+import OnlyDeclarative
+
+Item {
+
+ MiscUtils {
+ id: miscUtils
+ }
+
+ Component.onCompleted: {
+ const varlist = miscUtils.createVariantList();
+ const obj = { test: varlist };
+ const listProperty = miscUtils.createQmlListProperty();
+ miscUtils.logArray(varlist);
+ miscUtils.logObject(obj);
+ miscUtils.logArray(listProperty);
+ }
+}
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index ec0d1b4386..3c25d29dfb 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -36,8 +36,10 @@ public:
private slots:
void initTestCase() override;
void rootContext();
+#if QT_CONFIG(qml_network)
void networkAccessManager();
void synchronousNetworkAccessManager();
+#endif
void baseUrl();
void contextForObject();
void offlineStoragePath();
@@ -80,6 +82,7 @@ private slots:
void lockedRootObject();
void crossReferencingSingletonsDeletion();
void bindingInstallUseAfterFree();
+ void variantListQJsonConversion();
public slots:
QObject *createAQObjectForOwnershipTest ()
@@ -152,6 +155,7 @@ void tst_qqmlengine::rootContext()
QVERIFY(!engine.rootContext()->parentContext());
}
+#if QT_CONFIG(qml_network)
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
@@ -227,7 +231,7 @@ void tst_qqmlengine::synchronousNetworkAccessManager()
// reply is finished, so should not be in loading state.
QVERIFY(!c.isLoading());
}
-
+#endif
void tst_qqmlengine::baseUrl()
{
@@ -1734,6 +1738,21 @@ void tst_qqmlengine::bindingInstallUseAfterFree()
QVERIFY(o);
}
+void tst_qqmlengine::variantListQJsonConversion()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("variantListQJsonConversion.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"(["cpp","variant","list"])");
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"({"test":["cpp","variant","list"]})");
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg,
+ R"([{"objectName":"o0"},{"objectName":"o1"},{"objectName":"o2"}])");
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+}
+
QTEST_MAIN(tst_qqmlengine)
#include "tst_qqmlengine.moc"
diff --git a/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h
new file mode 100644
index 0000000000..edf2174a18
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTLIST_QJSON_CONVERSION_HPP
+#define VARIANTLIST_QJSON_CONVERSION_HPP
+
+#include "qqmlintegration.h"
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QObject>
+#include <QJsonDocument>
+#include <QDebug>
+#include <private/qjsvalue_p.h>
+#include <private/qqmllistwrapper_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4jsonobject_p.h>
+
+class MiscUtils : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ Q_INVOKABLE QVariantList createVariantList() const
+ {
+ return { QString("cpp"), QString("variant"), QString("list") };
+ }
+
+ Q_INVOKABLE QQmlListProperty<QObject> createQmlListProperty()
+ {
+ QV4::ExecutionEngine engine(qmlEngine(this));
+ static QObject objects[] = { QObject{}, QObject{}, QObject{} };
+ objects[0].setObjectName("o0");
+ objects[1].setObjectName("o1");
+ objects[2].setObjectName("o2");
+ static QList<QObject *> list{ &objects[0], &objects[1], &objects[2] };
+ return QQmlListProperty<QObject>(this, &list);
+ }
+
+ Q_INVOKABLE void logArray(const QJsonArray &arr) const
+ {
+ const auto str = QString(QJsonDocument(arr).toJson(QJsonDocument::Compact));
+ qDebug().noquote() << str;
+ }
+
+ Q_INVOKABLE void logObject(const QJsonObject &obj) const
+ {
+ const auto str = QString(QJsonDocument(obj).toJson(QJsonDocument::Compact));
+ qDebug().noquote() << str;
+ }
+};
+
+#endif // VARIANTLIST_QJSON_CONVERSION_HPP
diff --git a/tests/auto/qml/qqmljsscope/data/ownModuleName.qml b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml
new file mode 100644
index 0000000000..6e43ce6b05
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ Item { id: child }
+ component IC: Item {
+ Item {
+ id: childInIC
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
index db81c77206..4dacc17f94 100644
--- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
+++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
@@ -71,6 +71,7 @@ class tst_qqmljsscope : public QQmlDataTest
logger.setCode(sourceCode);
logger.setSilent(expectErrorsOrWarnings);
QQmlJSScope::Ptr target = QQmlJSScope::create();
+ target->setOwnModuleName(u"HelloModule"_s);
QQmlJSImportVisitor visitor(target, &m_importer, &logger, dataDirectory());
QQmlJSTypeResolver typeResolver { &m_importer };
typeResolver.init(&visitor, document->program);
@@ -108,6 +109,7 @@ private Q_SLOTS:
void extensions();
void emptyBlockBinding();
void hasOwnEnumerationKeys();
+ void ownModuleName();
void resolvedNonUniqueScopes();
void compilationUnitsAreCompatible();
void attachedTypeResolution_data();
@@ -356,7 +358,7 @@ void tst_qqmljsscope::descriptiveNameOfNull()
stored, property, QQmlJSRegisterContent::InvalidLookupIndex,
QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ScopeProperty,
QQmlJSScope::ConstPtr());
- QCOMPARE(unscoped.descriptiveName(), u"bar of (invalid type)::foo with type baz"_s);
+ QCOMPARE(unscoped.descriptiveName(), u"(invalid type)::foo with type baz (stored as bar)"_s);
}
void tst_qqmljsscope::groupedPropertiesConsistency()
@@ -709,6 +711,33 @@ void tst_qqmljsscope::hasOwnEnumerationKeys()
QVERIFY(!extended->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtension"_s));
}
+void tst_qqmljsscope::ownModuleName()
+{
+ const QString moduleName = u"HelloModule"_s;
+ QQmlJSScope::ConstPtr root = run(u"ownModuleName.qml"_s);
+ QVERIFY(root);
+ QCOMPARE(root->moduleName(), moduleName);
+ QCOMPARE(root->ownModuleName(), moduleName);
+
+ QCOMPARE(root->childScopes().size(), 2);
+ QQmlJSScope::ConstPtr child = root->childScopes().front();
+ QVERIFY(child);
+ // only root and inline components have own module names, but the child should be able to query
+ // its component's module Name via moduleName()
+ QCOMPARE(child->ownModuleName(), QString());
+ QCOMPARE(child->moduleName(), moduleName);
+
+ QQmlJSScope::ConstPtr ic = root->childScopes()[1];
+ QVERIFY(ic);
+ QCOMPARE(ic->ownModuleName(), moduleName);
+ QCOMPARE(ic->moduleName(), moduleName);
+
+ QQmlJSScope::ConstPtr icChild = ic->childScopes().front();
+ QVERIFY(icChild);
+ QCOMPARE(icChild->ownModuleName(), QString());
+ QCOMPARE(icChild->moduleName(), moduleName);
+}
+
void tst_qqmljsscope::resolvedNonUniqueScopes()
{
QQmlJSScope::ConstPtr root = run(u"resolvedNonUniqueScope.qml"_s);
diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
new file mode 100644
index 0000000000..7e6f6e8dd9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property var b;
+ property Component c: QtObject {}
+
+ function returnList(a: Component) : list<Component> { return [a] }
+
+ Component.onCompleted: b = { b: returnList(c) }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 61295ec940..2f382e8d8e 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -452,6 +452,8 @@ private slots:
void overrideDefaultProperty();
void enumScopes();
+ void typedObjectList();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -8642,6 +8644,21 @@ void tst_qqmllanguage::enumScopes()
QCOMPARE(o->property("singletonUnscopedValue").toInt(), int(EnumProviderSingleton::Expected::Value));
}
+void tst_qqmllanguage::typedObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("typedObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QVERIFY(list.at(&list, 0) != nullptr);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index dbab663b22..a7ddf79ad5 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -478,10 +478,12 @@ void tst_qqmllocale::toString_data()
QTest::newRow(qPrintable(functionCallScript)) << "ar" << functionCallScript << "١٦" << QString();
functionCallScript = "locale.toString(new Date(2022, 7, 16), Locale.ShortFormat)";
- QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 12:00 AM" << QString();
+ QTest::newRow(qPrintable(functionCallScript))
+ << "en_AU" << functionCallScript << "16/8/22 12:00 am" << QString();
functionCallScript = "locale.toString(new Date(2022, 7, 16, 1, 23, 4), Locale.ShortFormat)";
- QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 1:23 AM" << QString();
+ QTest::newRow(qPrintable(functionCallScript))
+ << "en_AU" << functionCallScript << "16/8/22 1:23 am" << QString();
}
void tst_qqmllocale::toString()
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index dea781cc17..04c2a5bfdb 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -769,8 +769,16 @@ void checkBuiltinTypes()
template<typename T>
void checkNamedBuiltin(const QString &name)
{
- QCOMPARE(QQmlMetaType::qmlType("QML/" + name, QTypeRevision::fromVersion(1, 0)),
- QQmlMetaType::qmlType(QMetaType::fromType<T>()));
+ const QQmlType expected = QQmlMetaType::qmlType(QMetaType::fromType<T>());
+ const QQmlType actual = QQmlMetaType::qmlType("QML/" + name, QTypeRevision::fromVersion(1, 0));
+ if (actual != expected) {
+ qWarning() << Q_FUNC_INFO << "looking for" << name;
+ qWarning() << "found" << actual.module() << actual.elementName() << actual.version()
+ << actual.typeId();
+ qWarning() << "expected" << expected.module() << expected.elementName()
+ << expected.version() << expected.typeId();
+ QFAIL("mismatch");
+ }
}
template<typename T>
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index 0e1bb1abc3..e57eb1b65a 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -131,7 +131,9 @@ void registerStaticPlugin(const char *uri)
PluginType::metaData.append(char(QT_VERSION_MAJOR));
PluginType::metaData.append(char(QT_VERSION_MINOR));
PluginType::metaData.append(char(qPluginArchRequirements()));
+#if QT_CONFIG(cborstreamwriter)
PluginType::metaData.append(QCborValue(QCborMap::fromJsonObject(md)).toCbor());
+#endif
auto rawMetaDataFunctor = []() -> QPluginMetaData {
return {reinterpret_cast<const uchar *>(PluginType::metaData.constData()), size_t(PluginType::metaData.size())};
diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
index 710bbce17a..9fea41104d 100644
--- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
+++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
@@ -57,8 +57,10 @@ private slots:
void alpha();
void tint();
void color();
+#if QT_CONFIG(desktopservices)
void openUrlExternally();
void openUrlExternally_pragmaLibrary();
+#endif
void md5();
void createComponent();
void createComponent_pragmaLibrary();
@@ -613,6 +615,7 @@ public slots:
void noteCall(const QUrl &url) { called++; last = url; }
};
+#if QT_CONFIG(desktopservices)
void tst_qqmlqt::openUrlExternally()
{
MyUrlHandler handler;
@@ -659,6 +662,7 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary()
QCOMPARE(handler.called,2);
QCOMPARE(handler.last, htmlTestFile);
}
+#endif
void tst_qqmlqt::md5()
{
diff --git a/tests/auto/qml/qv4mm/data/simpleObject.qml b/tests/auto/qml/qv4mm/data/simpleObject.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/tests/auto/qml/qv4mm/data/simpleObject.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 28926f02f3..5bcdcd4624 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -12,6 +12,7 @@
#include <private/qqmlengine_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4arraydata_p.h>
+#include <private/qqmlcomponentattached_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -34,6 +35,8 @@ private slots:
void cleanInternalClasses();
void createObjectsOnDestruction();
void sharedInternalClassDataMarking();
+ void gcTriggeredInOnDestroyed();
+ void weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues();
};
tst_qv4mm::tst_qv4mm()
@@ -387,6 +390,118 @@ void tst_qv4mm::sharedInternalClassDataMarking()
QCOMPARE(val.toUInt32(), 42u);
}
+void tst_qv4mm::gcTriggeredInOnDestroyed()
+{
+ QQmlEngine engine;
+ QV4::ExecutionEngine &v4 = *engine.handle();
+
+ QPointer<QObject> testObject = new QObject; // unparented, will be deleted
+ auto cleanup = qScopeGuard([&]() {
+ if (testObject)
+ testObject->deleteLater();
+ });
+
+ QQmlComponent component(&engine, testFileUrl("simpleObject.qml"));
+ auto toBeCollected = component.create();
+ QVERIFY(toBeCollected);
+ QJSEngine::setObjectOwnership(toBeCollected, QJSEngine::JavaScriptOwnership);
+ QV4::QObjectWrapper::ensureWrapper(&v4, toBeCollected);
+ QVERIFY(qmlEngine(toBeCollected));
+ QQmlComponentAttached *attached = QQmlComponent::qmlAttachedProperties(toBeCollected);
+ QVERIFY(attached);
+
+
+ QV4::Scope scope(v4.rootContext());
+ QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked);
+
+
+
+ // let the gc run up to CallDestroyObjects
+ auto sm = v4.memoryManager->gcStateMachine.get();
+ sm->reset();
+ v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked;
+ while (sm->state != QV4::GCState::CallDestroyObjects && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QCOMPARE(sm->state, QV4::GCState::CallDestroyObjects);
+
+ QV4::ScopedValue val(scope);
+ bool calledOnDestroyed = false;
+ auto con = connect(attached, &QQmlComponentAttached::destruction, this, [&]() {
+ calledOnDestroyed = true;
+ // we trigger uncommon code paths:
+ // create ObjectWrapper in destroyed hadnler
+ auto ddata = QQmlData::get(testObject.get(), false);
+ QVERIFY(!ddata); // we don't have ddata yet (otherwise we'd already have an object wrapper)
+ val = QV4::QObjectWrapper::wrap(&v4, testObject.get());
+ QJSEngine::setObjectOwnership(testObject, QJSEngine::JavaScriptOwnership);
+
+ // and also try to trigger a force gc completion
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(!gcComplete);
+ });
+ while (!calledOnDestroyed && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QVERIFY(!QTest::currentTestFailed());
+ QObject::disconnect(con);
+ QVERIFY(calledOnDestroyed);
+
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(gcComplete);
+ val = QV4::Value::undefinedValue(); // no longer keep a reference on the stack
+ QCOMPARE(sm->state, QV4::GCState::Invalid);
+ QVERIFY(testObject); // must not have be deleted, referenced by val
+
+ gc(v4); // run another gc cycle
+ QVERIFY(!testObject); // now collcted by gc
+}
+void tst_qv4mm::weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues()
+{
+ QObject testObject;
+ QV4::ExecutionEngine v4;
+
+ QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked);
+
+
+
+ // let the gc run up to CallDestroyObjects
+ auto sm = v4.memoryManager->gcStateMachine.get();
+ sm->reset();
+ v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked;
+
+
+ // run just before the sweeping face
+ while (sm->state != QV4::GCState::DoSweep && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QCOMPARE(sm->state, QV4::GCState::DoSweep);
+
+ {
+ // simulate code accessing the object wrapper for an object
+ QV4::Scope scope(v4.rootContext());
+ QV4::ScopedValue value(scope);
+ value = QV4::QObjectWrapper::wrap(&v4, &testObject);
+ // let it go out of scope before any stack re-scanning could happen
+ }
+
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(gcComplete);
+
+ auto ddata = QQmlData::get(&testObject);
+ QVERIFY(ddata);
+ if (ddata->jsWrapper.isUndefined()) {
+ // it's in principle valid for the wrapper to be reset, though the current
+ // implementation doesn't do it, and it requires some care
+ qWarning("Double-check the handling of weak values and object wrappers in the gc");
+ return;
+ }
+ QVERIFY(ddata->jsWrapper.valueRef()->heapObject()->inUse());
+}
+
QTEST_MAIN(tst_qv4mm)
#include "tst_qv4mm.moc"
diff --git a/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
new file mode 100644
index 0000000000..169d162469
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+
+}
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.h b/tests/auto/qmldom/domitem/tst_qmldomitem.h
index fb240b7c39..4464333b6e 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.h
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.h
@@ -549,6 +549,13 @@ private slots:
DomItem f2 = env.path(p2);
QVERIFY2(f2, "Directory dependencies did not load MySingleton.qml");
}
+ {
+ QString fPath = tFile.canonicalFilePath();
+ QString fPath2 = fPath.mid(0, fPath.lastIndexOf(u'/')) % u"/ImportMeImplicitly.ui.qml";
+ Path p2 = Paths::qmlFileObjectPath(fPath2);
+ DomItem f2 = env.path(p2);
+ QVERIFY2(f2, "Directory dependencies did not load .ui.qml file!");
+ }
}
void testImports()
diff --git a/tests/auto/qmlls/cli/tst_qmlls_cli.cpp b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
index f871decec1..344ed48e64 100644
--- a/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
+++ b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmlls_cli.h"
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
index 764a8f65cd..9145bb7566 100644
--- a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
@@ -874,7 +874,7 @@ void tst_qmlls_modules::renameUsages_data()
// TODO: create workspace edit for the tests
QLspSpecification::WorkspaceEdit sumRenames{
std::nullopt, // TODO
- QList<TextDocumentEdit>{
+ QList<QLspSpecification::WorkspaceEdit::DocumentChange>{
TextDocumentEdit{
OptionalVersionedTextDocumentIdentifier{ { jsIdentifierUsagesUri } },
{
@@ -907,6 +907,38 @@ void tst_qmlls_modules::renameUsages_data()
};
}
+void tst_qmlls_modules::compareQTextDocumentEdit(const TextDocumentEdit &a,
+ const TextDocumentEdit &b)
+{
+
+ QCOMPARE(a.textDocument.uri, b.textDocument.uri);
+ QVERIFY(a.textDocument.uri.startsWith("file://"));
+ QCOMPARE(a.textDocument.version, b.textDocument.version);
+ QCOMPARE(a.edits.size(), b.edits.size());
+
+ for (qsizetype j = 0; j < a.edits.size(); ++j) {
+ std::visit(
+ [](auto &&textEdit, auto &&expectedTextEdit) {
+ using U = std::decay_t<decltype(textEdit)>;
+ using V = std::decay_t<decltype(expectedTextEdit)>;
+
+ if constexpr (std::conjunction_v<std::is_same<U, V>,
+ std::is_same<U, TextEdit>>) {
+ QCOMPARE(textEdit.range.start.line, expectedTextEdit.range.start.line);
+ QCOMPARE(textEdit.range.start.character,
+ expectedTextEdit.range.start.character);
+ QCOMPARE(textEdit.range.end.line, expectedTextEdit.range.end.line);
+ QCOMPARE(textEdit.range.end.character,
+ expectedTextEdit.range.end.character);
+ QCOMPARE(textEdit.newText, expectedTextEdit.newText);
+ } else {
+ QFAIL("Comparison not implemented");
+ }
+ },
+ a.edits[j], b.edits[j]);
+ }
+}
+
void tst_qmlls_modules::renameUsages()
{
QFETCH(QString, filePath);
@@ -944,66 +976,24 @@ void tst_qmlls_modules::renameUsages()
QCOMPARE(result->documentChanges.has_value(),
expectedEdit.documentChanges.has_value());
- std::visit(
- [&expectedError](auto &&documentChanges, auto &&expectedDocumentChanges) {
- if (!expectedError.message.isEmpty())
- QVERIFY2(false, "No expected error was thrown.");
-
- QCOMPARE(documentChanges.size(), expectedDocumentChanges.size());
- using U = std::decay_t<decltype(documentChanges)>;
- using V = std::decay_t<decltype(expectedDocumentChanges)>;
-
- if constexpr (std::conjunction_v<
- std::is_same<U, V>,
- std::is_same<U, QList<TextDocumentEdit>>>) {
- for (qsizetype i = 0; i < expectedDocumentChanges.size(); ++i) {
- QCOMPARE(documentChanges[i].textDocument.uri,
- expectedDocumentChanges[i].textDocument.uri);
- QVERIFY(documentChanges[i].textDocument.uri.startsWith(
- "file://"));
- QCOMPARE(documentChanges[i].textDocument.version,
- expectedDocumentChanges[i].textDocument.version);
- QCOMPARE(documentChanges[i].edits.size(),
- expectedDocumentChanges[i].edits.size());
-
- for (qsizetype j = 0; j < documentChanges[i].edits.size();
- ++j) {
- std::visit(
- [](auto &&textEdit, auto &&expectedTextEdit) {
- using U = std::decay_t<decltype(textEdit)>;
- using V = std::decay_t<
- decltype(expectedTextEdit)>;
-
- if constexpr (std::conjunction_v<
- std::is_same<U, V>,
- std::is_same<U,
- TextEdit>>) {
- QCOMPARE(textEdit.range.start.line,
- expectedTextEdit.range.start.line);
- QCOMPARE(textEdit.range.start.character,
- expectedTextEdit.range.start
- .character);
- QCOMPARE(textEdit.range.end.line,
- expectedTextEdit.range.end.line);
- QCOMPARE(textEdit.range.end.character,
- expectedTextEdit.range.end
- .character);
- QCOMPARE(textEdit.newText,
- expectedTextEdit.newText);
- } else {
- QFAIL("Comparison not implemented");
- }
- },
- documentChanges[i].edits[j],
- expectedDocumentChanges[i].edits[j]);
- }
- }
+ auto &documentChanges = *result->documentChanges;
+ auto &expectedDocumentChanges = *expectedEdit.documentChanges;
- } else {
- QFAIL("Comparison not implemented");
- }
- },
- result->documentChanges.value(), expectedEdit.documentChanges.value());
+ if (!expectedError.message.isEmpty())
+ QVERIFY2(false, "No expected error was thrown.");
+
+ QCOMPARE(documentChanges.size(), expectedDocumentChanges.size());
+
+ for (qsizetype i = 0; i < expectedDocumentChanges.size(); ++i) {
+ QCOMPARE(documentChanges[i].index(), expectedDocumentChanges[i].index());
+ if (std::holds_alternative<TextDocumentEdit>(documentChanges[i])) {
+ compareQTextDocumentEdit(
+ std::get<TextDocumentEdit>(documentChanges[i]),
+ std::get<TextDocumentEdit>(expectedDocumentChanges[i]));
+ } else {
+ QFAIL("TODO: implement me!");
+ }
+ }
},
[clean, &expectedError](const ResponseError &err) {
QScopeGuard cleanup(clean);
@@ -1487,12 +1477,12 @@ void tst_qmlls_modules::quickFixes()
QVERIFY(codeAction.edit);
QVERIFY(codeAction.edit->documentChanges);
- QVERIFY(std::holds_alternative<QList<TextDocumentEdit>>(*codeAction.edit->documentChanges));
- auto edits = std::get<QList<TextDocumentEdit>>(*codeAction.edit->documentChanges);
+ const auto &edits = *codeAction.edit->documentChanges;
QCOMPARE(edits.size(), 1);
- QCOMPARE(edits.front().edits.size(), 1);
- QVERIFY(std::holds_alternative<TextEdit>(edits.front().edits.front()));
- auto textEdit = std::get<TextEdit>(edits.front().edits.front());
+ const auto& firstEdit = std::get<TextDocumentEdit>(edits.front());
+ QCOMPARE(firstEdit.edits.size(), 1);
+ QVERIFY(std::holds_alternative<TextEdit>(firstEdit.edits.front()));
+ auto textEdit = std::get<TextEdit>(firstEdit.edits.front());
// make sure that the quick fix does something
QCOMPARE(textEdit.newText, replacementText);
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.h b/tests/auto/qmlls/modules/tst_qmlls_modules.h
index 352af1d928..d48bc99407 100644
--- a/tests/auto/qmlls/modules/tst_qmlls_modules.h
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.h
@@ -37,6 +37,8 @@ public:
std::optional<QByteArray> openFile(const QString &uri);
std::optional<QByteArray> openFileFromAbsolutePath(const QString &uri);
void ignoreDiagnostics();
+ void compareQTextDocumentEdit(const QLspSpecification::TextDocumentEdit &a,
+ const QLspSpecification::TextDocumentEdit &b);
private slots:
void init() final;
void cleanup();
diff --git a/tests/auto/qmlls/qmlls/tst_qmlls.cpp b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
index 4507a4b8b9..9e057992c4 100644
--- a/tests/auto/qmlls/qmlls/tst_qmlls.cpp
+++ b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
@@ -219,12 +219,12 @@ void tst_Qmlls::didOpenTextDocument()
WorkspaceEdit edit = action.edit.value();
QVERIFY(edit.documentChanges.has_value());
- auto docChangeVariant = edit.documentChanges.value();
- QVERIFY(std::holds_alternative<QList<TextDocumentEdit>>(docChangeVariant));
- auto documentChanges = std::get<QList<TextDocumentEdit>>(docChangeVariant);
+ auto documentChanges = edit.documentChanges.value();
QCOMPARE(documentChanges.size(), 1);
- TextDocumentEdit textDocEdit = documentChanges.first();
+ QVERIFY(std::holds_alternative<TextDocumentEdit>(documentChanges.first()));
+ TextDocumentEdit textDocEdit
+ = std::get<TextDocumentEdit>(documentChanges.first());
QCOMPARE(textDocEdit.textDocument.uri, textDocument.uri);
QVERIFY(std::holds_alternative<int>(textDocEdit.textDocument.version));
diff --git a/tests/auto/quick/qquickitem2/CMakeLists.txt b/tests/auto/quick/qquickitem2/CMakeLists.txt
index 7034acc184..6b115efd2e 100644
--- a/tests/auto/quick/qquickitem2/CMakeLists.txt
+++ b/tests/auto/quick/qquickitem2/CMakeLists.txt
@@ -37,6 +37,11 @@ qt_internal_add_test(tst_qquickitem2
## Scopes:
#####################################################################
+qt_internal_extend_target(tst_qquickitem2 CONDITION TARGET Qt::Widgets
+ LIBRARIES
+ Qt::Widgets
+)
+
qt_internal_extend_target(tst_qquickitem2 CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/data"
diff --git a/tests/auto/quick/qquickitem2/data/embedded.qml b/tests/auto/quick/qquickitem2/data/embedded.qml
new file mode 100644
index 0000000000..a9cf115699
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/embedded.qml
@@ -0,0 +1,30 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 300
+
+ Column {
+ anchors.fill: parent
+ anchors.rightMargin: 2
+ anchors.leftMargin: 2
+ anchors.topMargin: 10
+ spacing: 20
+ Rectangle {
+ objectName: "rect1"
+ width: parent.width
+ height: 30
+ border.width: 1
+ border.color: activeFocus ? "blue" : "black"
+ focusPolicy: Qt.TabFocus
+ }
+ Rectangle {
+ objectName: "rect2"
+ width: parent.width
+ height: 30
+ border.width: 1
+ border.color: activeFocus ? "blue" : "black"
+ focusPolicy: Qt.TabFocus
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index 57deb0a46a..267be73ec9 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -23,6 +23,11 @@
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/platforminputcontext_p.h>
#include <QtTest/private/qpropertytesthelper_p.h>
+#ifdef QT_WIDGETS_LIB
+#include <QtWidgets/qwidget.h>
+#include <QtWidgets/qboxlayout.h>
+#include <QtWidgets/qlineedit.h>
+#endif
using namespace QQuickVisualTestUtils;
@@ -134,6 +139,10 @@ private slots:
void lastFocusChangeReason();
void focusInScopeChanges();
+#ifdef QT_WIDGETS_LIB
+ void embeddedInWidgetsFocus();
+#endif
+
private:
QQmlEngine engine;
bool qt_tab_all_widgets() {
@@ -4431,6 +4440,70 @@ void tst_QQuickItem::focusInScopeChanges()
QVERIFY(textInput->hasActiveFocus());
}
+#ifdef QT_WIDGETS_LIB
+void tst_QQuickItem::embeddedInWidgetsFocus()
+{
+ QWidget root;
+ QVBoxLayout *layout = new QVBoxLayout(&root);
+
+ QLineEdit *lineEdit1 = new QLineEdit(&root);
+ lineEdit1->setFocusPolicy(Qt::FocusPolicy::TabFocus);
+
+ QQuickView *quickView = new QQuickView;
+ quickView->setSource(testFileUrl("embedded.qml"));
+ QWidget *container = QWidget::createWindowContainer(quickView, &root);
+ container->setMinimumSize(quickView->size());
+ container->setFocusPolicy(Qt::TabFocus);
+
+ QLineEdit *lineEdit2 = new QLineEdit(&root);
+ lineEdit2->setFocusPolicy(Qt::FocusPolicy::TabFocus);
+
+ layout->addWidget(lineEdit1);
+ layout->addWidget(container);
+ layout->addWidget(lineEdit2);
+
+ QQuickItem *rect1 = findItem<QQuickItem>(quickView->rootObject(), "rect1");
+ QQuickItem *rect2 = findItem<QQuickItem>(quickView->rootObject(), "rect2");
+ QVERIFY(rect1);
+ QVERIFY(rect2);
+
+ root.show();
+ QTRY_VERIFY(root.isVisible());
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+
+ lineEdit1->setFocus();
+ QTRY_VERIFY(lineEdit1->hasFocus());
+
+ // Tab forward
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QTRY_VERIFY(container->hasFocus());
+ QVERIFY(QTest::qWaitForWindowFocused(quickView));
+ QVERIFY(rect1->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QTRY_VERIFY(rect2->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+ QVERIFY(lineEdit2->hasFocus());
+ QVERIFY(!rect2->hasActiveFocus());
+
+ // Tab backwards
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QTRY_VERIFY(container->hasFocus());
+ QVERIFY(QTest::qWaitForWindowFocused(quickView));
+ QVERIFY(rect2->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(rect1->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+ QVERIFY(lineEdit1->hasFocus());
+}
+#endif
+
QTEST_MAIN(tst_QQuickItem)
#include "tst_qquickitem.moc"
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
index 63734d9d14..d31a0b0fb8 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -945,7 +945,19 @@ Item {
},
layoutWidth: 0,
expectedWidths: [0]
- }
+ },{
+ tag: "preferred_infinity", // Do not crash/assert when the preferred size is infinity
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 10, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ {minimumWidth: 20, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ ]
+ },
+ layoutWidth: 31, // Important that this is between minimum and preferred width of the layout.
+ expectedWidths: [10, 21] // The result here does not have to be exact. (This
+ // test is mostly concerned about not crashing).
+ }
];
}
@@ -1567,6 +1579,49 @@ Item {
compare(rootRect.item1.width, 100)
}
+ //---------------------------
+ // Layout with negative size
+ Component {
+ id: negativeSize_Component
+ Item {
+ id: rootItem
+ width: 0
+ height: 0
+ // default width x height: (0 x 0)
+ RowLayout {
+ spacing: 0
+ anchors.fill: parent
+ anchors.leftMargin: 1 // since parent size == (0 x 0), it causes layout size
+ anchors.bottomMargin: 1 // to become (-1, -1)
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_negativeSize() {
+ let rootItem = createTemporaryObject(negativeSize_Component, container)
+ let rowLayout = rootItem.children[0]
+ let item = rowLayout.children[0]
+
+ const arr = [7, 1, 7, 0]
+ arr.forEach((n) => {
+ rootItem.width = n
+ rootItem.height = n
+
+ // n === 0 is special: It will cause the layout to have a
+ // negative size. In this case it will simply not rearrange its
+ // child (and leave it at its previous size, 6)
+ const expectedItemExtent = n === 0 ? 6 : n - 1
+
+ compare(item.width, expectedItemExtent)
+ compare(item.height, expectedItemExtent)
+ });
+ }
+
+
//---------------------------
Component {
id: rowlayoutWithTextItems_Component
diff --git a/tests/auto/quick/qquickpixmapcache/CMakeLists.txt b/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
index 1e1d35a8af..97735172f2 100644
--- a/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
+++ b/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
@@ -24,7 +24,6 @@ qt_internal_add_test(tst_qquickpixmapcache
tst_qquickpixmapcache.cpp
deviceloadingimage.h deviceloadingimage.cpp
LIBRARIES
- Qt::Concurrent
Qt::CorePrivate
Qt::Gui
Qt::GuiPrivate
@@ -48,6 +47,11 @@ qt_internal_extend_target(tst_qquickpixmapcache CONDITION NOT ANDROID AND NOT IO
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+qt_internal_extend_target(tst_qquickpixmapcache CONDITION QT_FEATURE_concurrent
+ LIBRARIES
+ Qt::Concurrent
+)
+
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(tst_qquickpixmapcache
URI PixmapCacheTest
diff --git a/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml b/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml
new file mode 100644
index 0000000000..af899ec5dd
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Window {
+
+}
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 1e2553c107..ff3edb3b64 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -549,6 +549,8 @@ private slots:
void visibleVsVisibility_data();
void visibleVsVisibility();
+ void visibilityDoesntClobberWindowState();
+
void eventTypes();
private:
@@ -4139,6 +4141,40 @@ void tst_qquickwindow::visibleVsVisibility()
QCOMPARE(window->isVisible(), expectVisible);
}
+void tst_qquickwindow::visibilityDoesntClobberWindowState()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("visibilityDoesntClobberWindowState.qml"));
+ QObject *created = component.create();
+ QScopedPointer<QObject> cleanup(created);
+ QVERIFY(created);
+
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(created);
+ QVERIFY(window);
+
+ window->showMaximized();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ QCOMPARE(window->windowState(), Qt::WindowMaximized);
+
+ window->setProperty("visible", false);
+ window->setProperty("visible", true);
+
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ QCOMPARE(window->windowState(), Qt::WindowMaximized);
+
+ EventFilter eventFilter;
+ window->installEventFilter(&eventFilter);
+ window->setProperty("visibility", QWindow::FullScreen);
+ QTRY_VERIFY(eventFilter.events.contains(QEvent::WindowStateChange));
+ QCOMPARE(window->windowState(), Qt::WindowFullScreen);
+
+ eventFilter.events.clear();
+ window->setWindowState(Qt::WindowMaximized);
+ QTRY_VERIFY(eventFilter.events.contains(QEvent::WindowStateChange));
+ QTRY_COMPARE(window->windowState(), Qt::WindowMaximized);
+}
+
void tst_qquickwindow::eventTypes()
{
QQmlEngine engine;
diff --git a/tests/auto/quickcontrols/controls/data/tst_dial.qml b/tests/auto/quickcontrols/controls/data/tst_dial.qml
index cfe901bc79..1f2b9fdd5c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_dial.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_dial.qml
@@ -826,4 +826,42 @@ TestCase {
compare(dial.endAngle, 300.)
}
}
+
+ function test_notSquareGeometry() {
+ let dial = createTemporaryObject(dialComponent, testCase)
+ verify(dial);
+ if (!dial.handle) {
+ skip("Test cannot run on styles where handle == null (macOS style)")
+ }
+ dial.from = 0
+ dial.to = 1
+ dial.live = true
+ dial.wrap = true
+ dial.startAngle = -180
+ dial.endAngle = 180
+
+ // Dial input handling always assumes that the dial is in the *center*.
+ // Instantiate a Dial with a geometries of 400x100 and then 100x400
+ // Some styles always could wrongly align the Dial background and handle in the topLeft
+ // corner. Pressing in the handle would cause the Dial to move because the dial
+ // assumes that the "Dial circle" is center aligned in its geometry.
+ for (let pass = 0; pass < 2; ++pass) {
+ if (pass === 0) {
+ dial.width = testCase.width
+ dial.height = 100
+ } else {
+ dial.width = 100
+ dial.height = testCase.height
+ }
+
+ let val = pass * 0.25
+ dial.value = val
+ // find coordinates in the middle of the handle
+ let pt2 = dial.mapFromItem(dial.handle, dial.handle.width/2, dial.handle.height/2)
+ // press the knob in the middle. It shouldn't move (except from due to rounding errors)
+ mousePress(dial, pt2.x, pt2.y)
+ fuzzyCompare(dial.value, val, 0.1)
+ }
+ }
+
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_popup.qml b/tests/auto/quickcontrols/controls/data/tst_popup.qml
index 1257148761..2428192961 100644
--- a/tests/auto/quickcontrols/controls/data/tst_popup.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_popup.qml
@@ -43,37 +43,39 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(popupControl, testCase)
verify(control)
}
function test_padding() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
- var paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
+ let paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
verify(paddingSpy.valid)
- var topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
+ let topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
verify(topPaddingSpy.valid)
- var leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
+ let leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
verify(leftPaddingSpy.valid)
- var rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
+ let rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
verify(rightPaddingSpy.valid)
- var bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
+ let bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
verify(bottomPaddingSpy.valid)
- var paddingChanges = 0
- var topPaddingChanges = 0
- var leftPaddingChanges = 0
- var rightPaddingChanges = 0
- var bottomPaddingChanges = 0
+ let paddingChanges = 0
+ let topPaddingChanges = 0
+ let leftPaddingChanges = 0
+ let rightPaddingChanges = 0
+ let bottomPaddingChanges = 0
compare(control.padding, 0)
compare(control.topPadding, 0)
@@ -160,17 +162,17 @@ TestCase {
}
function test_availableSize() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
- var availableWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableWidthChanged"})
+ let availableWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableWidthChanged"})
verify(availableWidthSpy.valid)
- var availableHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableHeightChanged"})
+ let availableHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableHeightChanged"})
verify(availableHeightSpy.valid)
- var availableWidthChanges = 0
- var availableHeightChanges = 0
+ let availableWidthChanges = 0
+ let availableHeightChanges = 0
control.width = 100
compare(control.availableWidth, 100)
@@ -230,14 +232,14 @@ TestCase {
}
function test_position() {
- var control = createTemporaryObject(popupControl, testCase, {visible: true, leftMargin: 10, topMargin: 20, width: 100, height: 100})
+ let control = createTemporaryObject(popupControl, testCase, {visible: true, leftMargin: 10, topMargin: 20, width: 100, height: 100})
verify(control)
verify(control.visible)
- var xSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "xChanged"})
+ let xSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "xChanged"})
verify(xSpy.valid)
- var ySpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "yChanged"})
+ let ySpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "yChanged"})
verify(ySpy.valid)
// moving outside margins does not trigger change notifiers
@@ -286,7 +288,7 @@ TestCase {
}
function test_resetSize() {
- var control = createTemporaryObject(popupControl, testCase, {visible: true, margins: 0})
+ let control = createTemporaryObject(popupControl, testCase, {visible: true, margins: 0})
verify(control)
control.scale = 1.0
@@ -303,7 +305,7 @@ TestCase {
}
function test_negativeMargins() {
- var control = createTemporaryObject(popupControl, testCase, {implicitWidth: testCase.width, implicitHeight: testCase.height})
+ let control = createTemporaryObject(popupControl, testCase, {implicitWidth: testCase.width, implicitHeight: testCase.height})
verify(control)
control.open()
@@ -325,7 +327,7 @@ TestCase {
}
function test_margins() {
- var control = createTemporaryObject(popupTemplate, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(popupTemplate, testCase, {width: 100, height: 100})
verify(control)
control.open()
@@ -411,7 +413,7 @@ TestCase {
}
function test_background() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
control.background = rect.createObject(testCase)
@@ -456,8 +458,8 @@ TestCase {
}
function getChild(control, objname, idx) {
- var index = idx
- for (var i = index+1; i < control.children.length; i++)
+ let index = idx
+ for (let i = index+1; i < control.children.length; i++)
{
if (control.children[i].objectName === objname) {
index = i
@@ -526,15 +528,15 @@ TestCase {
}
function test_font() { // QTBUG_50984, QTBUG-51696
- var window = createTemporaryObject(component, testCase)
+ let window = createTemporaryObject(component, testCase)
verify(window)
compare(window.font.pixelSize, 40)
compare(window.pane.font.pixelSize, 30)
compare(window.pane.button.font.pixelSize, 20)
compare(window.popup.font.pixelSize, 40)
- var idx1 = getChild(window.popup.listview.contentItem, "delegate", -1)
- var idx2 = getChild(window.popup.listview.contentItem, "delegate", idx1)
+ let idx1 = getChild(window.popup.listview.contentItem, "delegate", -1)
+ let idx2 = getChild(window.popup.listview.contentItem, "delegate", idx1)
window.popup.listview.contentItem.children[idx1].fontspy.clear()
window.popup.listview.contentItem.children[idx2].fontspy.clear()
window.popup.button.fontspy.clear()
@@ -650,7 +652,7 @@ TestCase {
function test_locale() { // QTBUG_50984
// test looking up natural locale from ancestors
- var control = createTemporaryObject(localeComponent, applicationWindow.contentItem)
+ let control = createTemporaryObject(localeComponent, applicationWindow.contentItem)
verify(control)
compare(control.locale.name, "en_US")
@@ -733,10 +735,10 @@ TestCase {
function test_locale_changes() { // QTBUG_50984
// test default locale and locale inheritance
- var control = createTemporaryObject(localeChangeComponent, applicationWindow.contentItem)
+ let control = createTemporaryObject(localeChangeComponent, applicationWindow.contentItem)
verify(control)
- var defaultLocale = Qt.locale()
+ let defaultLocale = Qt.locale()
compare(control.ApplicationWindow.window.locale.name, defaultLocale.name)
compare(control.locale.name, defaultLocale.name)
compare(control.button.locale.name, defaultLocale.name)
@@ -902,10 +904,10 @@ TestCase {
}
function test_size() {
- var control = createTemporaryObject(popupControl, testCase)
+ let control = createTemporaryObject(popupControl, testCase)
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -963,7 +965,7 @@ TestCase {
}
function test_visible() {
- var control = createTemporaryObject(popupTemplate, testCase, {visible: true})
+ let control = createTemporaryObject(popupTemplate, testCase, {visible: true})
verify(control)
// QTBUG-51989
@@ -1021,7 +1023,7 @@ TestCase {
}
function indexOf(array, item) {
- for (var idx = 0; idx < array.length; ++idx) {
+ for (let idx = 0; idx < array.length; ++idx) {
if (item === array[idx])
return idx;
}
@@ -1029,13 +1031,13 @@ TestCase {
}
function findOverlay(window, popup) {
- var item = popup.contentItem.parent
- var idx = indexOf(window.Overlay.overlay.children, item)
+ let item = popup.contentItem.parent
+ let idx = indexOf(window.Overlay.overlay.children, item)
return window.Overlay.overlay.children[idx - 1]
}
function test_overlay() {
- var window = createTemporaryObject(overlayTest, testCase)
+ let window = createTemporaryObject(overlayTest, testCase)
verify(window)
window.requestActivate()
@@ -1043,7 +1045,7 @@ TestCase {
compare(window.Overlay.overlay.children.length, 0)
- var firstOverlay = findOverlay(window, window.firstDrawer)
+ let firstOverlay = findOverlay(window, window.firstDrawer)
verify(!firstOverlay)
window.firstDrawer.open()
compare(window.Overlay.overlay.children.length, 2) // 1 drawer + 1 overlay
@@ -1054,7 +1056,7 @@ TestCase {
indexOf(window.Overlay.overlay.children, window.firstDrawer.contentItem.parent) - 1)
tryCompare(firstOverlay, "opacity", 1.0)
- var secondOverlay = findOverlay(window, window.secondDrawer)
+ let secondOverlay = findOverlay(window, window.secondDrawer)
verify(!secondOverlay)
window.secondDrawer.open()
compare(window.Overlay.overlay.children.length, 4) // 2 drawers + 2 overlays
@@ -1077,7 +1079,7 @@ TestCase {
verify(!secondOverlay)
compare(window.Overlay.overlay.children.length, 0)
- var modalOverlay = findOverlay(window, window.modalPopup)
+ let modalOverlay = findOverlay(window, window.modalPopup)
verify(!modalOverlay)
window.modalPopup.open()
modalOverlay = findOverlay(window, window.modalPopup)
@@ -1087,7 +1089,7 @@ TestCase {
tryCompare(modalOverlay, "opacity", 1.0)
compare(window.Overlay.overlay.children.length, 2) // 1 popup + 1 overlay
- var modelessOverlay = findOverlay(window, window.modelessPopup)
+ let modelessOverlay = findOverlay(window, window.modelessPopup)
verify(!modelessOverlay)
window.modelessPopup.open()
modelessOverlay = findOverlay(window, window.modelessPopup)
@@ -1136,10 +1138,10 @@ TestCase {
}
function test_attached_applicationwindow() {
- var control = createTemporaryObject(popupControl, applicationWindow.contentItem)
+ let control = createTemporaryObject(popupControl, applicationWindow.contentItem)
verify(control)
- var child = rect.createObject(control.contentItem)
+ let child = rect.createObject(control.contentItem)
compare(control.ApplicationWindow.window, applicationWindow)
compare(control.contentItem.ApplicationWindow.window, applicationWindow)
@@ -1160,14 +1162,14 @@ TestCase {
}
function test_openedClosed() {
- var control = createTemporaryObject(pausePopup, testCase)
+ let control = createTemporaryObject(pausePopup, testCase)
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
- var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
+ let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
verify(closedSpy.valid)
- var openedChangeSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "openedChanged"})
+ let openedChangeSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "openedChanged"})
verify(openedChangeSpy.valid)
control.open()
@@ -1215,15 +1217,15 @@ TestCase {
}
function test_xyBindingLoop() {
- var window = createTemporaryObject(xyBindingLoop, testCase)
- var control = window.popup
+ let window = createTemporaryObject(xyBindingLoop, testCase)
+ let control = window.popup
waitForRendering(control.contentItem)
compare(control.x, (control.parent.width - control.width) / 2)
compare(control.y, (control.parent.height - control.height) / 2)
}
function test_windowParent() {
- var control = createTemporaryObject(popupControl, applicationWindow, {width: 100, height: 100})
+ let control = createTemporaryObject(popupControl, applicationWindow, {width: 100, height: 100})
verify(control)
control.open()
@@ -1231,7 +1233,7 @@ TestCase {
}
function test_deferredBackgroundSize() {
- var control = createTemporaryObject(popupControl, testCase, {width: 200, height: 100})
+ let control = createTemporaryObject(popupControl, testCase, {width: 200, height: 100})
verify(control)
compare(control.background.width, 200 + (control.background.leftInset || 0) + (control.background.rightInset || 0))
@@ -1239,7 +1241,7 @@ TestCase {
}
function test_anchors() {
- var control = createTemporaryObject(popupControl, applicationWindow.contentItem.Overlay.overlay,
+ let control = createTemporaryObject(popupControl, applicationWindow.contentItem.Overlay.overlay,
{ visible: true, width: 100, height: 100 })
applicationWindow.visible = true
@@ -1254,10 +1256,10 @@ TestCase {
compare(control.x, 0)
compare(control.y, 0)
- var overlay = control.Overlay.overlay
+ let overlay = control.Overlay.overlay
verify(overlay)
- var centerInSpy = createTemporaryObject(signalSpy, testCase, { target: control.anchors, signalName: "centerInChanged" })
+ let centerInSpy = createTemporaryObject(signalSpy, testCase, { target: control.anchors, signalName: "centerInChanged" })
verify(centerInSpy.valid)
verify(overlay.width > 0)
@@ -1270,7 +1272,7 @@ TestCase {
compare(control.y, (overlay.height - (control.width * control.scale)) / 2)
// Ensure that it warns when trying to set it to an item that's not its parent.
- var anotherItem = createTemporaryObject(rect, applicationWindow.contentItem, { x: 100, y: 100, width: 50, height: 50 })
+ let anotherItem = createTemporaryObject(rect, applicationWindow.contentItem, { x: 100, y: 100, width: 50, height: 50 })
verify(anotherItem)
ignoreWarning(new RegExp(".*QML Popup: Popup can only be centered within its immediate parent or Overlay.overlay"))
@@ -1341,13 +1343,13 @@ TestCase {
function test_shortcut() {
// Tests that a Shortcut with Qt.WindowShortcut context
// that is declared within a Popup is activated.
- var window = createTemporaryObject(shortcutWindowComponent, testCase)
- var control = window.popup
+ let window = createTemporaryObject(shortcutWindowComponent, testCase)
+ let control = window.popup
window.requestActivate()
tryCompare(window, "active", true)
- var shortcutActivatedSpy = createTemporaryObject(signalSpy, testCase,
+ let shortcutActivatedSpy = createTemporaryObject(signalSpy, testCase,
{ target: window.shortcut, signalName: "activated"} )
verify(shortcutActivatedSpy.valid)
diff --git a/tests/auto/quickcontrols/focus/tst_focus.cpp b/tests/auto/quickcontrols/focus/tst_focus.cpp
index c6a82ebaba..5d745813dc 100644
--- a/tests/auto/quickcontrols/focus/tst_focus.cpp
+++ b/tests/auto/quickcontrols/focus/tst_focus.cpp
@@ -198,6 +198,7 @@ void tst_focus::policy()
control->setFocus(false);
QVERIFY(!control->hasActiveFocus());
+#if QT_CONFIG(wheelevent)
// Qt::WheelFocus
QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -212,6 +213,7 @@ void tst_focus::policy()
QGuiApplication::sendEvent(control, &wheelEvent);
QVERIFY(control->hasActiveFocus());
QVERIFY(!control->hasVisualFocus());
+#endif
}
void tst_focus::reason()
@@ -396,6 +398,7 @@ void tst_focus::reason()
customItem->setFocusReason(Qt::NoFocusReason);
customText->setFocusReason(Qt::NoFocusReason);
+#if QT_CONFIG(wheelevent)
// Wheel focus -> MouseFocusReason
QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -409,6 +412,7 @@ void tst_focus::reason()
QTRY_VERIFY(!customItem->hasActiveFocus());
QCOMPARE(customItem->focusReason(), Qt::PopupFocusReason);
QTest::keyClick(&view, Qt::Key_Escape); // close the popup
+#endif
}
void tst_focus::visualFocus()
@@ -502,6 +506,7 @@ void tst_focus::scope()
QVERIFY(child->hasActiveFocus());
QVERIFY(control->hasActiveFocus());
+#if QT_CONFIG(wheelevent)
// Qt::WheelFocus
QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -509,6 +514,7 @@ void tst_focus::scope()
QGuiApplication::sendEvent(control, &wheelEvent);
QVERIFY(!child->hasActiveFocus());
QVERIFY(control->hasActiveFocus());
+#endif
}
QTEST_MAIN(tst_focus)
diff --git a/tests/auto/quickcontrols/platform/tst_platform.cpp b/tests/auto/quickcontrols/platform/tst_platform.cpp
index eeb5e64486..dbcc383f03 100644
--- a/tests/auto/quickcontrols/platform/tst_platform.cpp
+++ b/tests/auto/quickcontrols/platform/tst_platform.cpp
@@ -10,14 +10,17 @@ class Setup : public QObject
{
Q_OBJECT
Q_PROPERTY(bool shortcutsSupported READ areShortcutsSupported CONSTANT FINAL)
-
- Q_PROPERTY(int shortcutInt MEMBER m_shortcutInt CONSTANT FINAL)
Q_PROPERTY(QString shortcutString MEMBER m_shortcutString CONSTANT FINAL)
+#if QT_CONFIG(shortcut)
+ Q_PROPERTY(int shortcutInt MEMBER m_shortcutInt CONSTANT FINAL)
Q_PROPERTY(QKeySequence shortcutKeySequence MEMBER m_shortcutKeySequence CONSTANT FINAL)
+#endif
- const int m_shortcutInt = QKeySequence::Print;
const QString m_shortcutString = u"CTRL+P"_s;
+#if QT_CONFIG(shortcut)
+ const int m_shortcutInt = QKeySequence::Print;
const QKeySequence m_shortcutKeySequence{ Qt::CTRL | Qt::Key_P };
+#endif
public:
bool areShortcutsSupported() const
diff --git a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
index cefb494c6f..5eb3895849 100644
--- a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
@@ -63,8 +63,10 @@ private slots:
void hover_data();
void hover();
+#if QT_CONFIG(wheelevent)
void wheel_data();
void wheel();
+#endif
void multiple();
@@ -617,6 +619,7 @@ void tst_QQuickDrawer::hover()
QVERIFY(!drawerItem->isHovered());
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickDrawer::wheel_data()
{
QTest::addColumn<QString>("source");
@@ -703,6 +706,7 @@ void tst_QQuickDrawer::wheel()
QVERIFY(qFuzzyCompare(drawerSlider->value(), oldDrawerValue)); // must not have moved
}
}
+#endif
void tst_QQuickDrawer::multiple()
{
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index e774d055e1..f894387672 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -68,12 +68,16 @@ private slots:
void activeFocusItemAfterWindowInactive();
void hover_data();
void hover();
+#if QT_CONFIG(wheelevent)
void wheel_data();
void wheel();
+#endif
void parentDestroyed();
void nested();
+#if QT_CONFIG(wheelevent)
void nestedWheel();
void nestedWheelWithOverlayParent();
+#endif
void modelessOnModalOnModeless();
void grabber();
void cursorShape();
@@ -1068,6 +1072,7 @@ void tst_QQuickPopup::hover()
QVERIFY(parentButton->isHovered());
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickPopup::wheel_data()
{
QTest::addColumn<QString>("source");
@@ -1181,6 +1186,7 @@ void tst_QQuickPopup::wheel()
QVERIFY(qFuzzyCompare(popupSlider->value(), oldPopupValue)); // must not have moved
}
}
+#endif
void tst_QQuickPopup::parentDestroyed()
{
@@ -1219,6 +1225,7 @@ void tst_QQuickPopup::nested()
QCOMPARE(modalPopup->isVisible(), true);
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickPopup::nestedWheel()
{
QQuickControlsApplicationHelper helper(this, QStringLiteral("nested-wheel.qml"));
@@ -1283,6 +1290,7 @@ void tst_QQuickPopup::nestedWheelWithOverlayParent()
// Wheel over the list view, verify that it scrolls
QTRY_COMPARE(listView->contentY(), 72.);
}
+#endif
void tst_QQuickPopup::modelessOnModalOnModeless()
{
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index aa462da5dc..f7f2bd8c85 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -118,8 +118,10 @@ private:
QDir oldCurrentDir;
+#if QT_CONFIG(shortcut)
const QKeySequence goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
const QKeySequence editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+#endif
};
QStringList tst_QQuickFileDialogImpl::tempDirExpectedVisibleFiles(DelegateOrderPolicy order) const
@@ -391,6 +393,7 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
// below fail due to it being hidden when it loses activeFocus.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
@@ -410,6 +413,7 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+#endif
}
void tst_QQuickFileDialogImpl::chooseFileViaEnter()
@@ -553,9 +557,11 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -614,8 +620,10 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -642,8 +650,10 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// The breadcrumbs should be visible after opening, not the text edit.
QVERIFY(!breadcrumbBar->textField()->isVisible());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// The text edit should show the directory that contains the last file that was selected.
QCOMPARE(breadcrumbBar->textField()->text(), tempDir.path());
@@ -677,8 +687,10 @@ void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
@@ -763,6 +775,7 @@ void tst_QQuickFileDialogImpl::goUp()
int expectedCurrentIndex = showDirsFirst ? 0 : 2;
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), expectedCurrentIndex);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
QDir tempParentDir(tempDir.path());
QVERIFY(tempParentDir.cdUp());
@@ -775,6 +788,7 @@ void tst_QQuickFileDialogImpl::goUp()
QVERIFY(expectedCurrentIndex != -1);
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempParentDir.path()), QUrl::fromLocalFile(tempDir.path()), expectedCurrentIndex);
}
+#endif
}
void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
@@ -786,8 +800,10 @@ void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
@@ -854,6 +870,7 @@ void tst_QQuickFileDialogImpl::goUpIntoLargeFolder()
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDirLargeSubDir.path()),
QUrl::fromLocalFile(largeTempDirLargeSubDir.path() + "/sub-dir000"), 0);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
QString failureMessage;
@@ -861,6 +878,7 @@ void tst_QQuickFileDialogImpl::goUpIntoLargeFolder()
largeTempDirPaths, failureMessage), qPrintable(failureMessage));
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDir.path()),
QUrl::fromLocalFile(largeTempDirLargeSubDir.path()), largeTempDirLargeSubDirIndex);
+#endif
}
void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
@@ -870,16 +888,20 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
OPEN_QUICK_DIALOG();
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+#if QT_CONFIG(shortcut)
// Ctrl+L shouldn't hide it.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// Cancel it with the escape key.
@@ -887,8 +909,10 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
QVERIFY(!breadcrumbBar->textField()->isVisible());
QVERIFY(dialogHelper.dialog->isVisible());
+#if QT_CONFIG(shortcut)
// Make it visible.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// Cancel it with the escape key again.
@@ -1187,10 +1211,12 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
+#endif
// Hide it with the escape key. The Open button should now be enabled.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
@@ -1254,8 +1280,10 @@ void tst_QQuickFileDialogImpl::fileMode()
COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
}
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
index 0c29dad3da..2afcd81a44 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
@@ -344,6 +344,7 @@ void tst_QQuickFolderDialogImpl::changeFolderViaTextEdit()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -356,6 +357,7 @@ void tst_QQuickFolderDialogImpl::changeFolderViaTextEdit()
// Enter the path to the folder in the text edit.
enterText(dialogHelper.window(), tempSubDir2.path());
QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir2.path());
+#endif
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
@@ -417,9 +419,11 @@ void tst_QQuickFolderDialogImpl::cancelDialogWhileTextEditHasFocus()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -471,6 +475,7 @@ void tst_QQuickFolderDialogImpl::goUp()
QTRY_VERIFY(findViewDelegateItem(folderDialogListView, 0, subDirDelegate));
QCOMPARE(subDirDelegate->isHighlighted(), true);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
@@ -478,6 +483,7 @@ void tst_QQuickFolderDialogImpl::goUp()
QVERIFY(tempParentDir.cdUp());
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempParentDir.path()));
COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempDir.path()));
+#endif
}
void tst_QQuickFolderDialogImpl::goUpWhileTextEditHasFocus()
@@ -610,27 +616,33 @@ void tst_QQuickFolderDialogImpl::keyAndShortcutHandling()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+#if QT_CONFIG(shortcut)
// Ctrl+L shouldn't hide it.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
+#endif
// Cancel it with the escape key.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
QVERIFY(!breadcrumbBar->textField()->isVisible());
QVERIFY(dialogHelper.dialog->isVisible());
+#if QT_CONFIG(shortcut)
// Make it visible.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
+#endif
// Cancel it with the escape key again.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
@@ -795,11 +807,13 @@ void tst_QQuickFolderDialogImpl::itemsDisabledWhenNecessary()
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
+#endif
// Hide it with the escape key. The Open button should now be enabled.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
diff --git a/tests/baseline/CMakeLists.txt b/tests/baseline/CMakeLists.txt
index f00e3f197d..346444d79b 100644
--- a/tests/baseline/CMakeLists.txt
+++ b/tests/baseline/CMakeLists.txt
@@ -7,6 +7,8 @@ endif()
# Special case: test includes the QBaselineTest module sources from qtbase directly
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../../qtbase/tests/baseline/shared")
- add_subdirectory(scenegraph)
- add_subdirectory(controls)
+ if(QT_FEATURE_processenvironment)
+ add_subdirectory(scenegraph)
+ add_subdirectory(controls)
+ endif()
endif()
diff --git a/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp b/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
index 8cf6aa5a73..3a360ce0c8 100644
--- a/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
@@ -78,8 +78,10 @@ private slots:
void isString();
void isUndefined_data();
void isUndefined();
+#if QT_DEPRECATED_SINCE(6, 9)
void isVariant_data();
void isVariant();
+#endif
void toBool_data();
void toBool();
void toDateTime_data();
@@ -547,6 +549,7 @@ void tst_QJSValue::isUndefined()
}
}
+#if QT_DEPRECATED_SINCE(6, 9)
void tst_QJSValue::isVariant_data()
{
defineStandardTestValues();
@@ -559,6 +562,7 @@ void tst_QJSValue::isVariant()
val.isVariant();
}
}
+#endif
void tst_QJSValue::toBool_data()
{
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index 14d637b9af..95845fea8e 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -35,3 +35,4 @@ if(TARGET Qt::QuickVectorImage)
add_subdirectory(quickvectorimage)
add_subdirectory(svg)
endif()
+add_subdirectory(e2e)
diff --git a/tests/manual/e2e/CMakeLists.txt b/tests/manual/e2e/CMakeLists.txt
new file mode 100644
index 0000000000..1876f5a2c4
--- /dev/null
+++ b/tests/manual/e2e/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(qml)
diff --git a/tests/manual/e2e/qml/CMakeLists.txt b/tests/manual/e2e/qml/CMakeLists.txt
new file mode 100644
index 0000000000..e9f349be5d
--- /dev/null
+++ b/tests/manual/e2e/qml/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_FEATURE_process)
+ add_subdirectory(qmlformat)
+endif()
diff --git a/tests/manual/e2e/qml/qmlformat/CMakeLists.txt b/tests/manual/e2e/qml/qmlformat/CMakeLists.txt
new file mode 100644
index 0000000000..b93b2b6543
--- /dev/null
+++ b/tests/manual/e2e/qml/qmlformat/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## e2e_qmlformat Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlformat LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(e2e_qmlformat
+ SOURCES
+ e2e_qmlformat.cpp
+ DEFINES
+ SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::TestPrivate
+ Qt::QuickTestUtilsPrivate
+ TIMEOUT 3000
+)
+
+add_dependencies(e2e_qmlformat Qt::qmlformat)
diff --git a/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp b/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp
new file mode 100644
index 0000000000..7c0e7a21a6
--- /dev/null
+++ b/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp
@@ -0,0 +1,309 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QDir>
+#include <QFile>
+#include <QProcess>
+#include <QString>
+#include <QTemporaryDir>
+#include <QtTest/private/qemulationdetector_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomlinewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+using namespace QQmlJS::Dom;
+
+class E2ETestQmlformat : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+ void testExample();
+ void testExample_data();
+ void normalizeExample();
+ void normalizeExample_data();
+#endif
+
+private:
+ //TODO(QTBUG-117849) refactor this helper function
+ QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr,
+ LineWriterOptions options = LineWriterOptions(),
+ WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare,
+ WriteOutChecks largeChecks = WriteOutCheck::None);
+
+ QString m_qmlformatPath;
+ QStringList m_excludedDirs;
+ QStringList m_invalidFiles;
+ QStringList m_ignoreFiles;
+
+ QStringList findFiles(const QDir &);
+ bool isInvalidFile(const QFileInfo &fileName) const;
+ bool isIgnoredFile(const QFileInfo &fileName) const;
+};
+
+void E2ETestQmlformat::initTestCase()
+{
+ // QQmlDataTest::initTestCase();
+ m_qmlformatPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlformat");
+#ifdef Q_OS_WIN
+ m_qmlformatPath += QLatin1String(".exe");
+#endif
+ if (!QFileInfo(m_qmlformatPath).exists()) {
+ QString message = QStringLiteral("qmlformat executable not found (looked for %0)").arg(m_qmlformatPath);
+ QFAIL(qPrintable(message));
+ }
+
+ // Add directories you want excluded here
+
+ // These snippets are not expected to run on their own.
+ m_excludedDirs << "doc/src/snippets/qml/visualdatamodel_rootindex";
+ m_excludedDirs << "doc/src/snippets/qml/qtbinding";
+ m_excludedDirs << "doc/src/snippets/qml/imports";
+ m_excludedDirs << "doc/src/snippets/qtquick1/visualdatamodel_rootindex";
+ m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding";
+ m_excludedDirs << "doc/src/snippets/qtquick1/imports";
+ m_excludedDirs << "tests/manual/v4";
+ m_excludedDirs << "tests/manual/qmllsformatter";
+ m_excludedDirs << "tests/auto/qml/ecmascripttests";
+ m_excludedDirs << "tests/auto/qml/qmllint";
+
+ // Add invalid files (i.e. files with syntax errors)
+ m_invalidFiles << "tests/auto/quick/qquickloader/data/InvalidSourceComponent.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/property.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/empty.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/missingObject.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/insertedSemicolon.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nonexistantProperty.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidRoot.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidID.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/questionDotEOF.qml";
+ m_invalidFiles << "tests/auto/qml/qquickfolderlistmodel/data/dummy.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml";
+
+ // Files that get changed:
+ // rewrite of import "bla/bla/.." to import "bla"
+ m_invalidFiles << "tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml";
+ // block -> object in internal update
+ m_invalidFiles << "tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml";
+ // removal of unsupported indexing of Object declaration
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/hangOnWarning.qml";
+ // removal of duplicated id
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/component.3.qml";
+ // Optional chains are not permitted on the left-hand-side in assignments
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/optionalChaining.LHS.qml";
+ // object literal with = assignements
+ m_invalidFiles << "tests/auto/quickcontrols/controls/data/tst_scrollbar.qml";
+
+ // These files rely on exact formatting
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml";
+
+ // These files are too big
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml";
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml";
+}
+
+QStringList E2ETestQmlformat::findFiles(const QDir &d)
+{
+ for (int ii = 0; ii < m_excludedDirs.size(); ++ii) {
+ QString s = m_excludedDirs.at(ii);
+ if (d.absolutePath().endsWith(s))
+ return QStringList();
+ }
+
+ QStringList rv;
+
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
+ QDir::Files);
+ for (const QString &file: files) {
+ QString absoluteFilePath = d.absoluteFilePath(file);
+ if (!isIgnoredFile(QFileInfo(absoluteFilePath)))
+ rv << absoluteFilePath;
+ }
+
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
+ QDir::NoSymLinks);
+ for (const QString &dir: dirs) {
+ QDir sub = d;
+ sub.cd(dir);
+ rv << findFiles(sub);
+ }
+
+ return rv;
+}
+
+bool E2ETestQmlformat::isInvalidFile(const QFileInfo &fileName) const
+{
+ for (const QString &invalidFile : m_invalidFiles) {
+ if (fileName.absoluteFilePath().endsWith(invalidFile))
+ return true;
+ }
+ return false;
+}
+
+bool E2ETestQmlformat::isIgnoredFile(const QFileInfo &fileName) const
+{
+ for (const QString &file : m_ignoreFiles) {
+ if (fileName.absoluteFilePath().endsWith(file))
+ return true;
+ }
+ return false;
+}
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+void E2ETestQmlformat::testExample_data()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Crashes in QEMU. (timeout)");
+ QTest::addColumn<QString>("file");
+
+ QString examples = QLatin1String(SRCDIR) + "/../../../../../examples/";
+ QString tests = QLatin1String(SRCDIR) + "/../../../../../tests/";
+
+ QStringList files;
+ files << findFiles(QDir(examples));
+ files << findFiles(QDir(tests));
+
+ for (const QString &file : files)
+ QTest::newRow(qPrintable(file)) << file;
+}
+
+void E2ETestQmlformat::normalizeExample_data()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Crashes in QEMU. (timeout)");
+ QTest::addColumn<QString>("file");
+
+ QString examples = QLatin1String(SRCDIR) + "/../../../../../examples/";
+ QString tests = QLatin1String(SRCDIR) + "/../../../../../tests/";
+
+ // normalizeExample is similar to testExample, so we test it only on nExamples + nTests
+ // files to avoid making too many
+ QStringList files;
+ const int nExamples = 10;
+ int i = 0;
+ for (const auto &f : findFiles(QDir(examples))) {
+ files << f;
+ if (++i == nExamples)
+ break;
+ }
+ const int nTests = 10;
+ i = 0;
+ for (const auto &f : findFiles(QDir(tests))) {
+ files << f;
+ if (++i == nTests)
+ break;
+ }
+
+ for (const QString &file : files)
+ QTest::newRow(qPrintable(file)) << file;
+}
+#endif
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+void E2ETestQmlformat::testExample()
+{
+ QFETCH(QString, file);
+ const bool isInvalid = isInvalidFile(QFileInfo(file));
+ bool wasSuccessful;
+ LineWriterOptions opts;
+ opts.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+ QString output = formatInMemory(file, &wasSuccessful, opts);
+
+ if (!isInvalid)
+ QVERIFY(wasSuccessful && !output.isEmpty());
+}
+
+void E2ETestQmlformat::normalizeExample()
+{
+ QFETCH(QString, file);
+ const bool isInvalid = isInvalidFile(QFileInfo(file));
+ bool wasSuccessful;
+ LineWriterOptions opts;
+ opts.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
+ QString output = formatInMemory(file, &wasSuccessful, opts);
+
+ if (!isInvalid)
+ QVERIFY(wasSuccessful && !output.isEmpty());
+}
+#endif
+
+QString E2ETestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucceed,
+ LineWriterOptions options, WriteOutChecks extraChecks,
+ WriteOutChecks largeChecks)
+{
+ auto env = DomEnvironment::create(
+ QStringList(), // as we load no dependencies we do not need any paths
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ DomItem tFile;
+ env->loadFile(FileToLoad::fromFileSystem(env, fileToFormat),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ env->loadPendingDependencies();
+ MutableDomItem myFile = tFile.field(Fields::currentItem);
+
+ bool writtenOut;
+ QString resultStr;
+ if (myFile.field(Fields::isValid).value().toBool()) {
+ WriteOutChecks checks = extraChecks;
+ const qsizetype largeFileSize = 32000;
+ if (tFile.field(Fields::code).value().toString().size() > largeFileSize)
+ checks = largeChecks;
+
+ QTextStream res(&resultStr);
+ LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"), options);
+ OutWriter ow(lw);
+ ow.indentNextlines = true;
+ DomItem qmlFile = tFile.field(Fields::currentItem);
+ writtenOut = qmlFile.writeOutForFile(ow, checks);
+ lw.eof();
+ res.flush();
+ }
+ if (didSucceed)
+ *didSucceed = writtenOut;
+ return resultStr;
+}
+
+QTEST_MAIN(E2ETestQmlformat)
+#include "e2e_qmlformat.moc"
diff --git a/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg b/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg
new file mode 100644
index 0000000000..9448fcb527
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg
@@ -0,0 +1,11 @@
+<svg width="900" height="600" viewBox="0 0 90 60" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <g id="capTest">
+ <polyline points="10,10 80,10" fill="none" stroke-width="8" stroke="black"/>
+ <polyline points="10,10 80,10" fill="none" stroke-width="0.5" stroke-linecap="butt" stroke="yellow"/>
+ </g>
+ </defs>
+ <use href="#capTest" stroke-linecap="butt"/>
+ <use href="#capTest" y="20" stroke-linecap="round"/>
+ <use href="#capTest" y=" 40" stroke-linecap="square"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg b/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg
new file mode 100644
index 0000000000..1d52d2d247
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg
@@ -0,0 +1,12 @@
+<svg width="900" height="300" viewBox="0 0 130 40" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <polyline id="letterz" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="2.5" stroke="black"/>
+ <polyline id="highlight" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="0.1" stroke="yellow"/>
+ </defs>
+ <use href="#letterz" stroke-linecap="butt"/>
+ <use href="#highlight"/>
+ <use href="#letterz" x="40" stroke-linecap="round"/>
+ <use href="#highlight" x="40"/>
+ <use href="#letterz" x=" 80" stroke-linecap="square"/>
+ <use href="#highlight" x="80"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_dash.svg b/tests/manual/svg/data/styling/stroking_dash.svg
new file mode 100644
index 0000000000..5fdefa4cce
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_dash.svg
@@ -0,0 +1,10 @@
+<svg width="460" height="200" viewBox="0 0 30 13" xmlns="http://www.w3.org/2000/svg">
+ <line x1="0" y1="1" x2="30" y2="1" stroke="black" stroke-dashoffset="2"/>
+ <line x1="0" y1="3" x2="30" y2="3" stroke="black" stroke-dasharray="4 2"/>
+<!--positive dashoffset pulls the dashes-->
+ <line x1="0" y1="5" x2="30" y2="5" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="2"/>
+ <line x1="0" y1="7" x2="30" y2="7" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="4"/>
+ <!--negative dashoffset pushs the dashes-->
+ <line x1="0" y1="9" x2="30" y2="9" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-2"/>
+ <line x1="0" y1="11" x2="30" y2="11" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-4"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg b/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg
new file mode 100644
index 0000000000..1da775a97e
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg
@@ -0,0 +1,12 @@
+<svg width="600" height="200" viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <g id="capTest">
+ <polyline points="1,8 1,2 7,2" fill="none" stroke-width="1" stroke="black"/>
+ <polyline points="1,8 1,2 7,2" fill="none" stroke-width="0.03" stroke="yellow"/>
+ </g>
+ </defs>
+
+ <use href="#capTest" x="1" stroke-linejoin="bevel"/>
+ <use href="#capTest" x="11" stroke-linejoin="round"/>
+ <use href="#capTest" x="21" stroke-linejoin="miter"/>
+</svg>
diff --git a/tests/manual/svg/svgmanager.cpp b/tests/manual/svg/svgmanager.cpp
index 2afac0399a..b8d49413a5 100644
--- a/tests/manual/svg/svgmanager.cpp
+++ b/tests/manual/svg/svgmanager.cpp
@@ -64,8 +64,6 @@ void SvgManager::setCurrentDirectory(const QString &newCurrentDirectory)
QString SvgManager::qmlSource() const
{
QTemporaryFile tempFile;
- tempFile.setAutoRemove(false);
-
if (tempFile.open()) {
QString name = tempFile.fileName();
{
diff --git a/tests/manual/windowembedding/CMakeLists.txt b/tests/manual/windowembedding/CMakeLists.txt
index f93283d68f..6ad5167186 100644
--- a/tests/manual/windowembedding/CMakeLists.txt
+++ b/tests/manual/windowembedding/CMakeLists.txt
@@ -4,12 +4,6 @@
cmake_minimum_required(VERSION 3.16)
project(windowembedding LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/windowembedding")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Widgets)
find_package(Qt6 QUIET OPTIONAL_COMPONENTS Multimedia)
@@ -77,9 +71,16 @@ if(APPLE)
endif()
install(TARGETS windowembedding
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(windowembedding)
+qt_generate_deploy_qml_app_script(
+ TARGET windowembedding
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/tools/qmljsrootgen/main.cpp b/tools/qmljsrootgen/main.cpp
index 353859c08f..acd375144e 100644
--- a/tools/qmljsrootgen/main.cpp
+++ b/tools/qmljsrootgen/main.cpp
@@ -142,8 +142,8 @@ static QString buildClass(const QJSManagedValue &value, QJsonArray *classes,
protoName = name.at(0).toUpper() + name.mid(1) + QStringLiteral("Prototype");
}
- auto it = seen->prototypes.find(protoName);
- if (it == seen->prototypes.end()) {
+ auto it = seen->prototypes.constFind(protoName);
+ if (it == seen->prototypes.cend()) {
seen->prototypes.insert(protoName, prototype.toJSValue());
buildClass(prototype, classes, seen, protoName);
} else if (!it->strictlyEquals(prototype.toJSValue())) {
@@ -312,8 +312,8 @@ static QString buildConstructor(const QJSManagedValue &constructor, QJsonArray *
Q_UNREACHABLE();
}
- auto it = seen->constructors.find(name);
- if (it == seen->constructors.end()) {
+ auto it = seen->constructors.constFind(name);
+ if (it == seen->constructors.cend()) {
seen->constructors.insert(name, constructor.toJSValue());
return buildClass(*constructed, classes, seen, name);
} else if (!constructor.strictlyEquals(QJSManagedValue(*it, constructor.engine()))) {
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index 0541203571..a310c3d3c6 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -273,7 +273,7 @@ int main(int argc, char **argv)
auto currentScope = QQmlJSScope::create();
if (parser.isSet(moduleOption))
- currentScope->setModuleName(parser.value(moduleOption));
+ currentScope->setOwnModuleName(parser.value(moduleOption));
QmltcVisitor visitor(currentScope, &importer, &logger,
QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);