aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/CMakeLists.txt53
-rw-r--r--src/qml/Qt6AndroidQmlMacros.cmake21
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake174
-rw-r--r--src/qml/Qt6QmlConfigExtras.cmake.in7
-rw-r--r--src/qml/Qt6QmlMacros.cmake1189
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp14
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h1
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob.cpp4
-rw-r--r--src/qml/animations/qparallelanimationgroupjob.cpp4
-rw-r--r--src/qml/common/qqmljsfixedpoolarray_p.h2
-rw-r--r--src/qml/common/qv4alloca_p.h6
-rw-r--r--src/qml/common/qv4compileddata_p.h401
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp262
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h8
-rw-r--r--src/qml/compiler/qv4codegen.cpp26
-rw-r--r--src/qml/compiler/qv4compiler.cpp61
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp30
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h24
-rw-r--r--src/qml/configure.cmake13
-rw-r--r--src/qml/debugger/qqmldebug.cpp13
-rw-r--r--src/qml/debugger/qqmldebugconnector.cpp2
-rw-r--r--src/qml/debugger/qqmldebugconnector_p.h1
-rw-r--r--src/qml/debugger/qqmldebugserver.cpp (renamed from src/qml/types/qqmlmodelindexvaluetype.cpp)29
-rw-r--r--src/qml/debugger/qqmldebugserver_p.h1
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces_p.h2
-rw-r--r--src/qml/debugger/qqmldebugtranslationprotocol_p.h22
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/doc/qtqml.qdocconf2
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt43
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/FunnySingleton.qml56
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/TemplateFile.qml3
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/doc/README.txt1
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/nested/way/down/File.qml3
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/some_old_thing.qml3
-rw-r--r--src/qml/doc/snippets/code/doc_src_qtqml.cmake2
-rw-r--r--src/qml/doc/snippets/qml/CMakeLists.txt11
-rw-r--r--src/qml/doc/snippets/qml/MajorProject-CMakeLists.txt23
-rw-r--r--src/qml/doc/snippets/qml/createQmlObject.qml15
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs3
-rw-r--r--src/qml/doc/snippets/qml/myProject-CMakeLists.txt13
-rw-r--r--src/qml/doc/snippets/qml/myimageprovider.txt15
-rw-r--r--src/qml/doc/snippets/qml/plugin.cpp.txt14
-rw-r--r--src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp71
-rw-r--r--src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsengine.cpp68
-rw-r--r--src/qml/doc/snippets/qtjavascript/integratingjswithcpp/qjsengine.cpp63
-rw-r--r--src/qml/doc/src/cmake/cmake-properties.qdoc187
-rw-r--r--src/qml/doc/src/cmake/cmake-variables.qdoc40
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc224
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_plugin.qdoc95
-rw-r--r--src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc36
-rw-r--r--src/qml/doc/src/cmake/qt_target_qml_sources.qdoc185
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc78
-rw-r--r--src/qml/doc/src/cppintegration/extending-tutorial.qdoc20
-rw-r--r--src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc185
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc88
-rw-r--r--src/qml/doc/src/javascript/finetuning.qdoc9
-rw-r--r--src/qml/doc/src/javascript/hostenvironment.qdoc12
-rw-r--r--src/qml/doc/src/javascript/imports.qdoc13
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc55
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc8
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/networktransparency.qdoc5
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc10
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc547
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc8
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc5
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/imports.qdoc6
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc7
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc32
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc2
-rw-r--r--src/qml/doc/src/qt6-changes.qdoc4
-rw-r--r--src/qml/doc/src/qtqml-writing-a-module.qdoc289
-rw-r--r--src/qml/doc/src/qtqml.qdoc10
-rw-r--r--src/qml/inlinecomponentutils_p.h56
-rw-r--r--src/qml/jit/qv4baselinejit.cpp4
-rw-r--r--src/qml/jsapi/qjsengine.cpp8
-rw-r--r--src/qml/jsapi/qjsengine_p.h4
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp2
-rw-r--r--src/qml/jsapi/qjsvalue.cpp10
-rw-r--r--src/qml/jsapi/qjsvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h2
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp4
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp6
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp11
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h1
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp40
-rw-r--r--src/qml/jsruntime/qv4engine.cpp78
-rw-r--r--src/qml/jsruntime/qv4engine_p.h11
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp85
-rw-r--r--src/qml/jsruntime/qv4function.cpp3
-rw-r--r--src/qml/jsruntime/qv4function_p.h1
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4global_p.h1
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp170
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h38
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h6
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp32
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp120
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h52
-rw-r--r--src/qml/jsruntime/qv4object.cpp61
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp4
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h2
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp23
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h1
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp38
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h3
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp5
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp1
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp12
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp6
-rw-r--r--src/qml/jsruntime/qv4string_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp28
-rw-r--r--src/qml/memory/qv4mm.cpp2
-rw-r--r--src/qml/parser/qqmljs.g12
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h4
-rw-r--r--src/qml/qml/ftw/qintrusivelist_p.h5
-rw-r--r--src/qml/qml/qqml.cpp375
-rw-r--r--src/qml/qml/qqmlanybinding_p.h24
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp7
-rw-r--r--src/qml/qml/qqmlbinding.cpp85
-rw-r--r--src/qml/qml/qqmlbinding_p.h3
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp31
-rw-r--r--src/qml/qml/qqmlboundsignal_p.h2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp163
-rw-r--r--src/qml/qml/qqmlcomponentattached_p.h1
-rw-r--r--src/qml/qml/qqmlcontext.cpp24
-rw-r--r--src/qml/qml/qqmlcontext.h2
-rw-r--r--src/qml/qml/qqmlcontextdata.cpp2
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp4
-rw-r--r--src/qml/qml/qqmldata_p.h13
-rw-r--r--src/qml/qml/qqmlengine.cpp175
-rw-r--r--src/qml/qml/qqmlengine_p.h8
-rw-r--r--src/qml/qml/qqmlexpression.cpp4
-rw-r--r--src/qml/qml/qqmlexpression_p.h1
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp30
-rw-r--r--src/qml/qml/qqmlextensionplugin.h6
-rw-r--r--src/qml/qml/qqmlfile.cpp148
-rw-r--r--src/qml/qml/qqmlfileselector.cpp9
-rw-r--r--src/qml/qml/qqmlguard_p.h10
-rw-r--r--src/qml/qml/qqmlimport.cpp84
-rw-r--r--src/qml/qml/qqmlimport_p.h28
-rw-r--r--src/qml/qml/qqmlincubator.cpp15
-rw-r--r--src/qml/qml/qqmlirloader.cpp11
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp34
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h16
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp17
-rw-r--r--src/qml/qml/qqmlmetatype.cpp114
-rw-r--r--src/qml/qml/qqmlmetatype_p.h11
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp5
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp268
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h13
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp6
-rw-r--r--src/qml/qml/qqmlpluginimporter.cpp64
-rw-r--r--src/qml/qml/qqmlproperty.cpp90
-rw-r--r--src/qml/qml/qqmlproperty_p.h7
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp214
-rw-r--r--src/qml/qml/qqmlpropertybinding_p.h202
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp48
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h9
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp13
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h140
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h1
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp87
-rw-r--r--src/qml/qml/qqmlpropertyvalueinterceptor_p.h1
-rw-r--r--src/qml/qml/qqmlproxymetaobject.cpp10
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp8
-rw-r--r--src/qml/qml/qqmlscriptstring.cpp2
-rw-r--r--src/qml/qml/qqmlscriptstring.h1
-rw-r--r--src/qml/qml/qqmltype.cpp9
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp198
-rw-r--r--src/qml/qml/qqmltypedata.cpp48
-rw-r--r--src/qml/qml/qqmltypeloader.cpp11
-rw-r--r--src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp2
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent_p.h1
-rw-r--r--src/qml/qml/qqmltypemodule.cpp17
-rw-r--r--src/qml/qml/qqmltypemodule_p.h40
-rw-r--r--src/qml/qml/qqmltypenamecache.cpp114
-rw-r--r--src/qml/qml/qqmltypenamecache_p.h127
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp22
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp25
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp33
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp97
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h2
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp19
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h2
-rw-r--r--src/qml/qmldirparser/qqmlimportresolver_p.h4
-rw-r--r--src/qml/types/qqmlbind.cpp8
-rw-r--r--src/qml/types/qqmlconnections.cpp33
-rw-r--r--src/qml/types/qqmlmodelindexvaluetype_p.h174
-rw-r--r--src/qml/util/qqmlpropertymap.cpp2
194 files changed, 6559 insertions, 2992 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index af4ebb510f..1cacf0d889 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -19,6 +19,15 @@ if (QT_FEATURE_qml_worker_script)
)
endif()
+set(extra_cmake_files)
+set(extra_cmake_includes)
+if(ANDROID)
+ list(APPEND extra_cmake_files
+ "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}AndroidQmlMacros.cmake")
+ list(APPEND extra_cmake_includes
+ "${INSTALL_CMAKE_NAMESPACE}AndroidQmlMacros.cmake")
+endif()
+
qt_internal_add_qml_module(Qml
URI "QtQml"
VERSION "${PROJECT_VERSION}"
@@ -381,10 +390,21 @@ qt_internal_add_qml_module(Qml
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}qmldirTemplate.cmake.in"
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}QmlPluginTemplate.cpp.in"
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}QmlFindQmlscInternal.cmake"
+ ${extra_cmake_files}
EXTRA_CMAKE_INCLUDES
"${INSTALL_CMAKE_NAMESPACE}QmlFindQmlscInternal.cmake"
+ ${extra_cmake_includes}
)
+# Linking to the static qml plugin should also automatically link to the worker script
+# static plugin otherwise you get errors like
+# module "QtQml.WorkerScript" plugin "workerscriptplugin" not found
+# import QtQuick 2.0
+# ^
+if(QT_FEATURE_qml_worker_script)
+ _qt_internal_add_qml_static_plugin_dependency(qmlplugin workerscriptplugin)
+endif()
+
# special case begin remove the block, handled manually
# QLALR Grammars:
#qt_process_qlalr(
@@ -478,22 +498,6 @@ qt_internal_extend_target(Qml CONDITION QT_FEATURE_qml_animation
animations
)
-#### Keys ignored in scope 18:.:common:common/common.pri:NOT build_pass:
-# compile_hash_contents = "// Generated file, DO NOT EDIT" "$${LITERAL_HASH}define QML_COMPILE_HASH "$$QML_COMPILE_HASH"" "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)"
-# tag = <EMPTY>
-# tagFile = "$$PWD/../../.tag"
-
-#### Keys ignored in scope 19:.:common:common/common.pri:EXISTS _ss_tagFile:
-# QMAKE_INTERNAL_INCLUDED_FILES = "$$tagFile"
-# tag = "$$cat($$tagFile, singleline)"
-
-#### Keys ignored in scope 20:.:common:common/common.pri:NOT tag___equals____ss_{LITERAL_DOLLAR}Format AND %H_ss_{LITERAL_DOLLAR}:
-# QML_COMPILE_HASH = "$$tag"
-
-#### Keys ignored in scope 22:.:common:common/common.pri:EXISTS _ss_PWD/../../.git:
-# QML_COMPILE_HASH = "$$commit"
-# commit = "$$system(git rev-parse HEAD)"
-
qt_internal_extend_target(Qml CONDITION GCC AND QT_COMPILER_VERSION_MAJOR STREQUAL 5
COMPILE_OPTIONS
-fno-strict-aliasing
@@ -506,7 +510,7 @@ qt_internal_extend_target(Qml CONDITION QT_FEATURE_qml_debug
debugger/qqmldebug.cpp
debugger/qqmldebugconnector.cpp
debugger/qqmldebugpluginmanager_p.h
- debugger/qqmldebugserver_p.h
+ debugger/qqmldebugserver.cpp debugger/qqmldebugserver_p.h
debugger/qqmldebugserverconnection_p.h
debugger/qqmldebugservice.cpp debugger/qqmldebugservice_p.h
debugger/qqmldebugservicefactory_p.h
@@ -572,11 +576,6 @@ qt_internal_extend_target(Qml CONDITION hpux-_x_ OR solaris-_x_ OR (QT_FEATURE_c
rt
)
-qt_internal_extend_target(Qml CONDITION QT_FEATURE_qml_itemmodel
- SOURCES
- types/qqmlmodelindexvaluetype.cpp types/qqmlmodelindexvaluetype_p.h
-)
-
qt_internal_extend_target(Qml CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
WTF_USE_UDIS86=1
@@ -669,3 +668,13 @@ qt_internal_create_tracepoints(Qml qtqml.tracepoints)
qt_internal_add_docs(Qml
doc/qtqml.qdocconf
)
+
+# include snippet projects for developer shared builds
+# static builds fail with
+# CMake Error: AUTOMOC for target qt_target_qml_sources_example_resources_3:
+# The "moc" executable does not exist
+if(QT_FEATURE_private_tests AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.19" AND QT_BUILD_SHARED_LIBS)
+ add_subdirectory(doc/snippets/cmake/qt_target_qml_sources)
+ qt_autogen_tools(qt_target_qml_sources_example ENABLE_AUTOGEN_TOOLS moc)
+ qt_autogen_tools(qt_target_qml_sources_exampleplugin ENABLE_AUTOGEN_TOOLS moc)
+endif()
diff --git a/src/qml/Qt6AndroidQmlMacros.cmake b/src/qml/Qt6AndroidQmlMacros.cmake
new file mode 100644
index 0000000000..12be515531
--- /dev/null
+++ b/src/qml/Qt6AndroidQmlMacros.cmake
@@ -0,0 +1,21 @@
+# The function collects qml root paths and sets the QT_QML_ROOT_PATH property to the ${target}
+# based on the provided qml source files.
+function(_qt_internal_collect_qml_root_paths target)
+ get_target_property(qml_root_paths ${target} QT_QML_ROOT_PATH)
+ if(NOT qml_root_paths)
+ set(qml_root_paths "")
+ endif()
+ foreach(file IN LISTS ARGN)
+ get_filename_component(extension "${file}" LAST_EXT)
+ if(NOT extension STREQUAL ".qml")
+ continue()
+ endif()
+
+ get_filename_component(dir "${file}" DIRECTORY)
+ get_filename_component(absolute_dir "${dir}" ABSOLUTE)
+ list(APPEND qml_root_paths "${absolute_dir}")
+ endforeach()
+
+ list(REMOVE_DUPLICATES qml_root_paths)
+ set_target_properties(${target} PROPERTIES QT_QML_ROOT_PATH "${qml_root_paths}")
+endfunction()
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index ea5d3606ce..c84a7a9d90 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -4,31 +4,12 @@
include_guard(GLOBAL)
-# This function is essentially a wrapper around qt6_add_qml_module().
-# It creates the targets explicitly and sets up internal properties before
-# passing those targets to qt6_add_qml_module() for further updates.
-# All keywords supported by qt_internal_add_module() can be used, as can most
-# keywords for qt6_add_qml_module() except RESOURCE_PREFIX and
-# OUTPUT_TARGETS.
-#
-# OUTPUT_DIRECTORY and INSTALL_DIRECTORY will be given more appropriate defaults
-# if not provided by the caller. The defaults are usually what you want to use.
-#
-# - SOURCES is only passed through to qt_internal_add_plugin() or
-# qt_internal_add_module() but not to qt6_add_qml_module().
-#
-# See qt_internal_add_plugin() and qt6_add_qml_module() for the full set of
-# supported keywords.
-function(qt_internal_add_qml_module target)
-
- qt_internal_get_internal_add_module_keywords(
- module_option_args
- module_single_args
- module_multi_args
- )
-
- set(qml_module_option_args
+macro(qt_internal_get_internal_add_qml_module_keywords
+ option_args single_args multi_args
+ internal_option_args internal_single_args internal_multi_args)
+ set(${option_args}
DESIGNER_SUPPORTED
+ NO_PLUGIN
NO_PLUGIN_OPTIONAL
NO_CREATE_PLUGIN_TARGET
NO_GENERATE_PLUGIN_SOURCE
@@ -37,15 +18,7 @@ function(qt_internal_add_qml_module target)
NO_LINT
NO_CACHEGEN
)
- # TODO: Remove these once all repos have been updated to not use them
- set(ignore_option_args
- SKIP_TYPE_REGISTRATION # Now always done
- PLUGIN_OPTIONAL # Now the default
- GENERATE_QMLTYPES # Now the default
- INSTALL_QMLTYPES # Now the default
- )
-
- set(qml_module_single_args
+ set(${single_args}
URI
VERSION
PLUGIN_TARGET
@@ -54,9 +27,7 @@ function(qt_internal_add_qml_module target)
CLASSNAME # TODO: Remove once all other repos have been updated to use
# CLASS_NAME instead.
)
-
- set(qml_module_multi_args
- # SOURCES will be handled by qt_internal_add_module()
+ set(${multi_args}
QML_FILES
RESOURCES
IMPORTS
@@ -65,7 +36,6 @@ function(qt_internal_add_qml_module target)
DEPENDENCIES
PAST_MAJOR_VERSIONS
)
-
# Args used by qt_internal_add_qml_module directly, which should not be passed to any other
# functions.
#
@@ -73,28 +43,71 @@ function(qt_internal_add_qml_module target)
# installed.
#
# INSTALL_SOURCE_QMLDIR takes a path to an existing qmldir file that should be installed.
- set(internal_option_args
+ set(${internal_option_args}
)
-
- set(internal_single_args
+ set(${internal_single_args}
INSTALL_SOURCE_QMLTYPES
INSTALL_SOURCE_QMLDIR
)
+ set(${internal_multi_args}
+ )
+
+endmacro()
+
+# This function is essentially a wrapper around qt6_add_qml_module().
+# It creates the targets explicitly and sets up internal properties before
+# passing those targets to qt6_add_qml_module() for further updates.
+# All keywords supported by qt_internal_add_module() can be used, as can most
+# keywords for qt6_add_qml_module() except RESOURCE_PREFIX and
+# OUTPUT_TARGETS.
+#
+# OUTPUT_DIRECTORY and INSTALL_DIRECTORY will be given more appropriate defaults
+# if not provided by the caller. The defaults are usually what you want to use.
+#
+# - SOURCES is only passed through to qt_internal_add_plugin() or
+# qt_internal_add_module() but not to qt6_add_qml_module().
+#
+# See qt_internal_add_plugin() and qt6_add_qml_module() for the full set of
+# supported keywords.
+function(qt_internal_add_qml_module target)
+
+ qt_internal_get_internal_add_module_keywords(
+ module_option_args
+ module_single_args
+ module_multi_args
+ )
+
+ qt_internal_get_internal_add_qml_module_keywords(
+ qml_module_option_args
+ qml_module_single_args
+ qml_module_multi_args
+ qml_module_internal_option_args
+ qml_module_internal_single_args
+ qml_module_internal_multi_args
+ )
+ # TODO: Remove these once all repos have been updated to not use them
+ set(ignore_option_args
+ SKIP_TYPE_REGISTRATION # Now always done
+ PLUGIN_OPTIONAL # Now the default
+ GENERATE_QMLTYPES # Now the default
+ INSTALL_QMLTYPES # Now the default
+ )
set(option_args
${module_option_args}
${qml_module_option_args}
${ignore_option_args}
- ${internal_option_args}
+ ${qml_module_internal_option_args}
)
set(single_args
${module_single_args}
${qml_module_single_args}
- ${internal_single_args}
+ ${qml_module_internal_single_args}
)
set(multi_args
${module_multi_args}
${qml_module_multi_args}
+ ${qml_module_internal_multi_args}
)
qt_parse_all_arguments(arg "qt_internal_add_qml_module"
@@ -112,7 +125,9 @@ function(qt_internal_add_qml_module target)
if(NOT arg_INSTALL_DIRECTORY)
set(arg_INSTALL_DIRECTORY "${INSTALL_QMLDIR}/${target_path}")
endif()
- if(NOT arg_PLUGIN_TARGET)
+ if(arg_NO_PLUGIN)
+ unset(arg_PLUGIN_TARGET)
+ elseif(NOT arg_PLUGIN_TARGET)
set(arg_PLUGIN_TARGET ${target}plugin)
endif()
@@ -129,7 +144,7 @@ function(qt_internal_add_qml_module target)
endif()
set(plugin_args "")
- if(NOT arg_PLUGIN_TARGET STREQUAL target)
+ if(arg_NO_PLUGIN OR NOT arg_PLUGIN_TARGET STREQUAL target)
# Allow using an existing backing target.
if(NOT TARGET ${target})
# Create the backing target now to handle module-related things
@@ -139,8 +154,9 @@ function(qt_internal_add_qml_module target)
${qml_module_option_args}
${qml_module_single_args}
${qml_module_multi_args}
- ${internal_option_args}
- ${internal_single_args}
+ ${qml_module_internal_option_args}
+ ${qml_module_internal_single_args}
+ ${qml_module_internal_multi_args}
OUTPUT_DIRECTORY
INSTALL_DIRECTORY
ALL_ARGS
@@ -151,6 +167,9 @@ function(qt_internal_add_qml_module target)
${ARGN}
)
qt_internal_add_module(${target} ${module_args})
+ elseif(arg_SOURCES)
+ # If a module target was pre-created, we still need to pass the additional sources.
+ target_sources(${target} PRIVATE ${arg_SOURCES})
endif()
else()
# Since we are not creating a separate backing target, we have to pass
@@ -181,7 +200,9 @@ function(qt_internal_add_qml_module target)
)
endif()
- if(NOT arg_NO_CREATE_PLUGIN_TARGET)
+ set(add_qml_module_args "")
+
+ if(NOT arg_NO_PLUGIN AND NOT arg_NO_CREATE_PLUGIN_TARGET)
# If the qt_internal_add_qml_module call didn't specify a CLASS_NAME, we need to pre-compute
# it here and pass it along to qt_internal_add_plugin -> qt_add_plugin so that
# qt_add_qml_plugin does not complain about differing class names (the default pre-computed
@@ -192,7 +213,7 @@ function(qt_internal_add_qml_module target)
# Create plugin target now so we can set internal things
list(APPEND plugin_args
- TYPE qml_plugin
+ PLUGIN_TYPE qml_plugin
DEFAULT_IF FALSE
OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}
INSTALL_DIRECTORY ${arg_INSTALL_DIRECTORY}
@@ -201,6 +222,40 @@ function(qt_internal_add_qml_module target)
qt_internal_add_plugin(${arg_PLUGIN_TARGET} ${plugin_args})
+ # Use the plugin target name as the main part of the plugin basename.
+ set(plugin_basename "${arg_PLUGIN_TARGET}")
+
+ # If the target name already ends with a "plugin" suffix, remove it and re-add it to the end
+ # of the base name after the infix.
+ if(plugin_basename MATCHES "(.+)plugin$")
+ set(plugin_basename "${CMAKE_MATCH_1}")
+ endif()
+
+ # Add a the infix if Qt was configured with one.
+ if(QT_LIBINFIX)
+ string(APPEND plugin_basename "${QT_LIBINFIX}")
+ endif()
+
+ # Add the "plugin" suffix after the infix.
+ string(APPEND plugin_basename "plugin")
+
+ # Lowercase the whole thing and use it as the basename of the plugin library.
+ string(TOLOWER "${plugin_basename}" plugin_basename)
+ set_target_properties(${arg_PLUGIN_TARGET} PROPERTIES
+ OUTPUT_NAME "${plugin_basename}"
+ )
+
+ get_target_property(export_name ${arg_PLUGIN_TARGET} EXPORT_NAME)
+ if(export_name)
+ list(APPEND add_qml_module_args
+ INSTALLED_PLUGIN_TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${export_name}"
+ )
+ else()
+ list(APPEND add_qml_module_args
+ INSTALLED_PLUGIN_TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${arg_PLUGIN_TARGET}"
+ )
+ endif()
+
if(NOT arg_PLUGIN_TARGET STREQUAL target)
get_target_property(lib_type ${arg_PLUGIN_TARGET} TYPE)
if(lib_type STREQUAL "STATIC_LIBRARY")
@@ -217,14 +272,6 @@ function(qt_internal_add_qml_module target)
)
endif()
endif()
-
- # FIXME: Some repos expect this to be set and use it to install other
- # things relative to it. They should just specify the install
- # location directly. Once the other repos have been updated to
- # not rely on this, remove this property.
- set_target_properties(${arg_PLUGIN_TARGET} PROPERTIES
- QT_QML_MODULE_INSTALL_DIR ${arg_INSTALL_DIRECTORY}
- )
endif()
# TODO: Check if we need arg_SOURCES in this condition
@@ -246,13 +293,10 @@ function(qt_internal_add_qml_module target)
endif()
endforeach()
- if(QT_LIBINFIX)
- list(APPEND add_qml_module_args __QT_INTERNAL_QT_LIBINFIX "${QT_LIBINFIX}")
- endif()
-
# Update the backing and plugin targets with qml-specific things.
qt6_add_qml_module(${target}
${add_qml_module_args}
+ __QT_INTERNAL_INSTALL_METATYPES_JSON
OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}
RESOURCE_PREFIX "/qt-project.org/imports"
OUTPUT_TARGETS output_targets
@@ -329,7 +373,7 @@ function(qt_internal_add_qml_module target)
if(NOT arg_NO_GENERATE_QMLTYPES)
qt_install(
- FILES ${arg_OUTPUT_DIRECTORY}/$<TARGET_PROPERTY:${target},QT_QMLTYPES_FILENAME>
+ FILES ${arg_OUTPUT_DIRECTORY}/$<TARGET_PROPERTY:${target},QT_QML_MODULE_TYPEINFO>
DESTINATION "${arg_INSTALL_DIRECTORY}"
)
endif()
@@ -342,10 +386,6 @@ function(qt_internal_add_qml_module target)
endif()
if(arg_INSTALL_SOURCE_QMLTYPES)
- message(AUTHOR_WARNING
- "INSTALL_SOURCE_QMLTYPES option is deprecated and should not be used. "
- "Please port your module to use declarative type registration.")
-
set(files ${arg_INSTALL_SOURCE_QMLTYPES})
if(QT_WILL_INSTALL)
install(
@@ -361,10 +401,6 @@ function(qt_internal_add_qml_module target)
endif()
if(arg_INSTALL_SOURCE_QMLDIR)
- message(AUTHOR_WARNING
- "INSTALL_SOURCE_QMLDIR option is deprecated and should not be used. "
- "Please port your module to use declarative type registration.")
-
set(files ${arg_INSTALL_SOURCE_QMLDIR})
if(QT_WILL_INSTALL)
install(
diff --git a/src/qml/Qt6QmlConfigExtras.cmake.in b/src/qml/Qt6QmlConfigExtras.cmake.in
index 3d5be2c22c..194dc9eef2 100644
--- a/src/qml/Qt6QmlConfigExtras.cmake.in
+++ b/src/qml/Qt6QmlConfigExtras.cmake.in
@@ -11,3 +11,10 @@ if(NOT QT_NO_CREATE_TARGETS AND
qt@PROJECT_VERSION_MAJOR@_import_qml_plugins
)
endif()
+
+if(ANDROID)
+ # Set the default staging path of qml modules when building for Android
+ if("${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "")
+ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/android-qml")
+ endif()
+endif()
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index fe6078e58b..bda7b9b83a 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -9,6 +9,7 @@ function(qt6_add_qml_module target)
STATIC
SHARED
DESIGNER_SUPPORTED
+ NO_PLUGIN
NO_PLUGIN_OPTIONAL
NO_CREATE_PLUGIN_TARGET
NO_GENERATE_PLUGIN_SOURCE
@@ -16,12 +17,18 @@ function(qt6_add_qml_module target)
NO_GENERATE_QMLDIR
NO_LINT
NO_CACHEGEN
+ NO_RESOURCE_TARGET_PATH
# TODO: Remove once all usages have also been removed
SKIP_TYPE_REGISTRATION
+
+ # Used only by _qt_internal_qml_type_registration()
+ # TODO: Remove this once qt6_extract_metatypes does not install by default.
+ __QT_INTERNAL_INSTALL_METATYPES_JSON
)
set(args_single
PLUGIN_TARGET
+ INSTALLED_PLUGIN_TARGET # Internal option only, it may be removed
OUTPUT_TARGETS
RESOURCE_PREFIX
URI
@@ -36,7 +43,6 @@ function(qt6_add_qml_module target)
RESOURCE_EXPORT
INSTALL_DIRECTORY
INSTALL_LOCATION
- __QT_INTERNAL_QT_LIBINFIX # Used only by _qt_internal_target_generate_qmldir()
)
set(args_multi
@@ -116,25 +122,140 @@ function(qt6_add_qml_module target)
)
endif()
- # Provide defaults for options that have one
+ # Other arguments and checking for invalid combinations
if (NOT arg_TARGET_PATH)
+ # NOTE: This will always be used for copying things to the build
+ # directory, but it will not be used for resource paths if
+ # NO_RESOURCE_TARGET_PATH was given.
string(REPLACE "." "/" arg_TARGET_PATH ${arg_URI})
endif()
+ if(arg_NO_PLUGIN AND DEFINED arg_PLUGIN_TARGET)
+ message(FATAL_ERROR
+ "NO_PLUGIN was specified, but PLUGIN_TARGET was also given. "
+ "At most one of these can be present."
+ )
+ endif()
+
set(is_executable FALSE)
if(TARGET ${target})
+ if(arg_STATIC OR arg_SHARED)
+ message(FATAL_ERROR
+ "Cannot use STATIC or SHARED keyword when passed an existing target (${target})"
+ )
+ endif()
+
+ # With CMake 3.17 and earlier, a source file's generated property isn't
+ # visible outside of the directory scope in which it is set. That can
+ # lead to build errors for things like type registration due to CMake
+ # thinking nothing will create a missing file on the first run. With
+ # CMake 3.18 or later, we can force that visibility. Policy CMP0118
+ # added in CMake 3.20 should have made this unnecessary, but we can't
+ # rely on that because the user project controls what it is set to at
+ # the point where it matters, which is the end of the target's
+ # directory scope (hence we can't even test for it here).
+ get_target_property(source_dir ${target} SOURCE_DIR)
+ if(NOT source_dir STREQUAL CMAKE_CURRENT_SOURCE_DIR AND
+ CMAKE_VERSION VERSION_LESS "3.18")
+ message(WARNING
+ "qt6_add_qml_module() is being called in a different "
+ "directory scope to the one in which the target \"${target}\" "
+ "was created. CMake 3.18 or later is required to generate a "
+ "project robustly for this scenario, but you are using "
+ "CMake ${CMAKE_VERSION}. Ideally, qt6_add_qml_module() should "
+ "only be called from the same scope as the one the target was "
+ "created in to avoid dependency and visibility problems."
+ )
+ endif()
+
get_target_property(backing_target_type ${target} TYPE)
get_target_property(is_android_executable "${target}" _qt_is_android_executable)
if (backing_target_type STREQUAL "EXECUTABLE" OR is_android_executable)
+ if(DEFINED arg_PLUGIN_TARGET)
+ message(FATAL_ERROR
+ "A QML module with an executable as its backing target "
+ "cannot have a plugin."
+ )
+ endif()
+ if(arg_NO_CREATE_PLUGIN_TARGET)
+ message(WARNING
+ "A QML module with an executable as its backing target "
+ "cannot have a plugin. The NO_CREATE_PLUGIN_TARGET option "
+ "has no effect and should be removed."
+ )
+ endif()
+ set(arg_NO_PLUGIN TRUE)
+ set(lib_type "")
set(is_executable TRUE)
+ elseif(arg_NO_RESOURCE_TARGET_PATH)
+ message(FATAL_ERROR
+ "NO_RESOURCE_TARGET_PATH cannot be used for a backing target "
+ "that is not an executable"
+ )
+ elseif(backing_target_type STREQUAL "STATIC_LIBRARY")
+ set(lib_type STATIC)
+ elseif(backing_target_type MATCHES "(SHARED|MODULE)_LIBRARY")
+ set(lib_type SHARED)
+ else()
+ message(FATAL_ERROR "Unsupported backing target type: ${backing_target_type}")
+ endif()
+ else()
+ if(arg_STATIC AND arg_SHARED)
+ message(FATAL_ERROR
+ "Both STATIC and SHARED specified, at most one can be given"
+ )
+ endif()
+
+ if(arg_NO_RESOURCE_TARGET_PATH)
+ message(FATAL_ERROR
+ "NO_RESOURCE_TARGET_PATH can only be provided when an existing "
+ "executable target is passed in as the backing target"
+ )
+ endif()
+
+ # Explicit arguments take precedence, otherwise default to using the same
+ # staticality as what Qt was built with. This follows the already
+ # established default behavior for building ordinary Qt plugins.
+ # We don't allow the standard CMake BUILD_SHARED_LIBS variable to control
+ # the default because that can lead to different defaults depending on
+ # whether you build with a separate backing target or not.
+ if(arg_STATIC)
+ set(lib_type STATIC)
+ elseif(arg_SHARED)
+ set(lib_type SHARED)
+ elseif(QT6_IS_SHARED_LIBS_BUILD)
+ set(lib_type SHARED)
+ else()
+ set(lib_type STATIC)
endif()
endif()
- if(NOT arg_NO_CREATE_PLUGIN_TARGET AND NOT DEFINED arg_PLUGIN_TARGET AND NOT is_executable)
+ if(arg_NO_PLUGIN)
+ # Simplifies things a bit further below
+ set(arg_PLUGIN_TARGET "")
+ elseif(NOT DEFINED arg_PLUGIN_TARGET)
+ if(arg_NO_CREATE_PLUGIN_TARGET)
+ # We technically could allow this and rely on the project using the
+ # default plugin target name, but not doing so gives us the
+ # flexibility to potentially change that default later if needed.
+ message(FATAL_ERROR
+ "PLUGIN_TARGET must also be provided when NO_CREATE_PLUGIN_TARGET "
+ "is used. If you want to disable creating a plugin altogether, "
+ "use the NO_PLUGIN option instead."
+ )
+ endif()
set(arg_PLUGIN_TARGET ${target}plugin)
endif()
- if(NOT DEFINED arg_PLUGIN_TARGET)
- set(arg_PLUGIN_TARGET "") # Simplifies things a bit further below
+ if(arg_NO_CREATE_PLUGIN_TARGET AND arg_PLUGIN_TARGET STREQUAL target AND NOT TARGET ${target})
+ message(FATAL_ERROR
+ "PLUGIN_TARGET is the same as the backing target, which is allowed, "
+ "but NO_CREATE_PLUGIN_TARGET was also given and the target does not "
+ "exist. Either ensure the target is already created or do not "
+ "specify NO_CREATE_PLUGIN_TARGET."
+ )
+ endif()
+ if(NOT arg_INSTALLED_PLUGIN_TARGET)
+ set(arg_INSTALLED_PLUGIN_TARGET ${arg_PLUGIN_TARGET})
endif()
set(no_gen_source)
@@ -142,13 +263,6 @@ function(qt6_add_qml_module target)
set(no_gen_source NO_GENERATE_PLUGIN_SOURCE)
endif()
- set(lib_type "")
- if(arg_STATIC)
- set(lib_type STATIC)
- elseif(arg_SHARED)
- set(lib_type SHARED)
- endif()
-
if(arg_OUTPUT_DIRECTORY)
get_filename_component(arg_OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}"
ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}"
@@ -156,6 +270,18 @@ function(qt6_add_qml_module target)
else()
if("${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "")
set(arg_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ # For libraries, we assume/require that the source directory
+ # structure is consistent with the target path. For executables,
+ # the source directory will usually not reflect the target path
+ # and the project will often expect to be able to use resource
+ # paths that don't include the target path (they need the
+ # NO_RESOURCE_TARGET_PATH option if they do that). Tooling always
+ # needs the target path in the file system though, so the output
+ # directory should always have it. Handle the special case for
+ # executables to ensure this is what we get.
+ if(is_executable)
+ string(APPEND arg_OUTPUT_DIRECTORY "/${arg_TARGET_PATH}")
+ endif()
else()
if(NOT IS_ABSOLUTE "${QT_QML_OUTPUT_DIRECTORY}")
message(FATAL_ERROR
@@ -163,10 +289,29 @@ function(qt6_add_qml_module target)
"${QT_QML_OUTPUT_DIRECTORY}"
)
endif()
+ # This inherently does what we want for libraries and executables
set(arg_OUTPUT_DIRECTORY ${QT_QML_OUTPUT_DIRECTORY}/${arg_TARGET_PATH})
endif()
endif()
+ # Sanity check that we are not trying to have two different QML modules use
+ # the same output directory.
+ get_property(dirs GLOBAL PROPERTY _qt_all_qml_output_dirs)
+ if(dirs)
+ list(FIND dirs "${arg_OUTPUT_DIRECTORY}" index)
+ if(NOT index EQUAL -1)
+ get_property(qml_targets GLOBAL PROPERTY _qt_all_qml_targets)
+ list(GET qml_targets ${index} other_target)
+ message(FATAL_ERROR
+ "Output directory for target \"${target}\" is already used by "
+ "another QML module (target \"${other_target}\"). "
+ "Output directory is:\n ${arg_OUTPUT_DIRECTORY}\n"
+ )
+ endif()
+ endif()
+ set_property(GLOBAL APPEND PROPERTY _qt_all_qml_output_dirs ${arg_OUTPUT_DIRECTORY})
+ set_property(GLOBAL APPEND PROPERTY _qt_all_qml_targets ${target})
+
# TODO: Support for old keyword, remove once all repos no longer use CLASSNAME
if(arg_CLASSNAME)
if(arg_CLASS_NAME AND NOT arg_CLASSNAME STREQUAL arg_CLASS_NAME)
@@ -190,11 +335,6 @@ function(qt6_add_qml_module target)
endif()
if(TARGET ${target})
- if(arg_STATIC OR arg_SHARED)
- message(FATAL_ERROR
- "Cannot use STATIC or SHARED keyword when passed an existing target (${target})"
- )
- endif()
if(arg_PLUGIN_TARGET STREQUAL target)
# Insert the plugin's URI into its meta data to enable usage
# of static plugins in QtDeclarative (like in mkspecs/features/qml_plugin.prf).
@@ -203,21 +343,7 @@ function(qt6_add_qml_module target)
)
endif()
else()
- if(arg_STATIC AND arg_SHARED)
- message(FATAL_ERROR
- "Both STATIC and SHARED specified, at most one can be given"
- )
- endif()
-
if(arg_PLUGIN_TARGET STREQUAL target)
- if(arg_NO_CREATE_PLUGIN_TARGET AND NOT TARGET ${target})
- message(FATAL_ERROR
- "NO_CREATE_PLUGIN_TARGET was given, but PLUGIN_TARGET is "
- "the same as the backing target (which is allowed) and the "
- "target does not exist. Either ensure the target is already "
- "created or do not specify NO_CREATE_PLUGIN_TARGET."
- )
- endif()
qt6_add_qml_plugin(${target}
${lib_type}
${no_gen_source}
@@ -226,11 +352,7 @@ function(qt6_add_qml_module target)
CLASS_NAME ${arg_CLASS_NAME}
)
else()
- add_library(${target} ${lib_type})
- if(ANDROID)
- # TODO: Check if we need to do this for a backing library
- qt6_android_apply_arch_suffix(${target})
- endif()
+ qt6_add_library(${target} ${lib_type})
endif()
endif()
@@ -242,19 +364,6 @@ function(qt6_add_qml_module target)
set(arg_TYPEINFO ${target}.qmltypes)
endif()
- # Make the prefix conform to the following:
- # - Starts with a "/"
- # - Does not end with a "/" unless the prefix is exactly "/"
- if(NOT arg_RESOURCE_PREFIX)
- set(arg_RESOURCE_PREFIX "/")
- endif()
- if(NOT arg_RESOURCE_PREFIX MATCHES "^/")
- string(PREPEND arg_RESOURCE_PREFIX "/")
- endif()
- if(arg_RESOURCE_PREFIX MATCHES [[(.*)/$]])
- set(arg_RESOURCE_PREFIX "${CMAKE_MATCH_1}")
- endif()
-
foreach(import_set IN ITEMS IMPORTS OPTIONAL_IMPORTS)
foreach(import IN LISTS arg_${import_set})
string(FIND ${import} "/" slash_position REVERSE)
@@ -303,28 +412,53 @@ function(qt6_add_qml_module target)
endif()
endforeach()
- set(qt_qml_module_resource_prefix "${arg_RESOURCE_PREFIX}/${arg_TARGET_PATH}")
+ _qt_internal_canonicalize_resource_path("${arg_RESOURCE_PREFIX}" arg_RESOURCE_PREFIX)
+ if(arg_NO_RESOURCE_TARGET_PATH)
+ set(qt_qml_module_resource_prefix "${arg_RESOURCE_PREFIX}")
+ else()
+ if(arg_RESOURCE_PREFIX STREQUAL "/") # Checked so we prevent double-slash
+ set(qt_qml_module_resource_prefix "/${arg_TARGET_PATH}")
+ else()
+ set(qt_qml_module_resource_prefix "${arg_RESOURCE_PREFIX}/${arg_TARGET_PATH}")
+ endif()
+ endif()
set_target_properties(${target} PROPERTIES
QT_QML_MODULE_NO_LINT "${arg_NO_LINT}"
QT_QML_MODULE_NO_CACHEGEN "${arg_NO_CACHEGEN}"
QT_QML_MODULE_NO_GENERATE_QMLDIR "${arg_NO_GENERATE_QMLDIR}"
+ QT_QML_MODULE_NO_PLUGIN "${arg_NO_PLUGIN}"
QT_QML_MODULE_NO_PLUGIN_OPTIONAL "${arg_NO_PLUGIN_OPTIONAL}"
QT_QML_MODULE_URI "${arg_URI}"
QT_QML_MODULE_TARGET_PATH "${arg_TARGET_PATH}"
QT_QML_MODULE_VERSION "${arg_VERSION}"
QT_QML_MODULE_CLASS_NAME "${arg_CLASS_NAME}"
- QT_QML_MODULE_LIBINFIX "${arg___QT_INTERNAL_QT_LIBINFIX}"
+
QT_QML_MODULE_PLUGIN_TARGET "${arg_PLUGIN_TARGET}"
+ QT_QML_MODULE_INSTALLED_PLUGIN_TARGET "${arg_INSTALLED_PLUGIN_TARGET}"
+
+ # Also Save the PLUGIN_TARGET values in a separate property to circumvent
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/21484 when exporting the properties
+ _qt_qml_module_plugin_target "${arg_PLUGIN_TARGET}"
+ _qt_qml_module_installed_plugin_target "${arg_INSTALLED_PLUGIN_TARGET}"
+
QT_QML_MODULE_DESIGNER_SUPPORTED "${arg_DESIGNER_SUPPORTED}"
- QT_QML_MODULE_OUTPUT_DIR "${arg_OUTPUT_DIRECTORY}"
+ QT_QML_MODULE_OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}"
QT_QML_MODULE_RESOURCE_PREFIX "${qt_qml_module_resource_prefix}"
- QT_QML_PAST_MAJOR_VERSIONS "${arg_PAST_MAJOR_VERSIONS}"
- QT_QMLTYPES_FILENAME "${arg_TYPEINFO}"
+ QT_QML_MODULE_PAST_MAJOR_VERSIONS "${arg_PAST_MAJOR_VERSIONS}"
+ QT_QML_MODULE_TYPEINFO "${arg_TYPEINFO}"
# TODO: Check how this is used by qt6_android_generate_deployment_settings()
QT_QML_IMPORT_PATH "${arg_IMPORT_PATH}"
)
+
+ # Executables don't have a plugin target, so no need to export the properties.
+ if(NOT backing_target_type STREQUAL "EXECUTABLE" AND NOT is_android_executable)
+ set_property(TARGET ${target} APPEND PROPERTY
+ EXPORT_PROPERTIES _qt_qml_module_plugin_target _qt_qml_module_installed_plugin_target
+ )
+ endif()
+
set(ensure_set_properties
QT_QML_MODULE_PLUGIN_TYPES_FILE
QT_QML_MODULE_RESOURCE_PATHS
@@ -340,7 +474,11 @@ function(qt6_add_qml_module target)
endforeach()
if(NOT arg_NO_GENERATE_QMLTYPES)
- qt6_qml_type_registration(${target})
+ set(type_registration_extra_args "")
+ if(arg___QT_INTERNAL_INSTALL_METATYPES_JSON)
+ list(APPEND type_registration_extra_args __QT_INTERNAL_INSTALL_METATYPES_JSON)
+ endif()
+ _qt_internal_qml_type_registration(${target} ${type_registration_extra_args})
endif()
set(output_targets)
@@ -363,25 +501,52 @@ function(qt6_add_qml_module target)
# if Qt was built with CMake or qmake, while the build system transition phase is still
# happening.
string(REPLACE "/" "_" qmldir_resource_name "qmake_${arg_TARGET_PATH}")
+
+ # The qmldir file ALWAYS has to be under the target path, even in the
+ # resources. If it isn't, an explicit import can't find it. We need a
+ # second copy NOT under the target path if NO_RESOURCE_TARGET_PATH is
+ # given so that the implicit import will work.
+ set(prefixes "${qt_qml_module_resource_prefix}")
+ if(arg_NO_RESOURCE_TARGET_PATH)
+ # The above prefixes item won't include the target path, so add a
+ # second one that does.
+ if(qt_qml_module_resource_prefix STREQUAL "/")
+ list(APPEND prefixes "/${arg_TARGET_PATH}")
+ else()
+ list(APPEND prefixes "${qt_qml_module_resource_prefix}/${arg_TARGET_PATH}")
+ endif()
+ endif()
set_source_files_properties(${arg_OUTPUT_DIRECTORY}/qmldir
PROPERTIES QT_RESOURCE_ALIAS "qmldir"
)
- set(resource_targets)
- qt6_add_resources(${target} ${qmldir_resource_name}
- FILES ${arg_OUTPUT_DIRECTORY}/qmldir
- PREFIX "${qt_qml_module_resource_prefix}"
- OUTPUT_TARGETS resource_targets
- )
- list(APPEND output_targets ${resource_targets})
+
+ foreach(prefix IN LISTS prefixes)
+ set(resource_targets)
+ qt6_add_resources(${target} ${qmldir_resource_name}
+ FILES ${arg_OUTPUT_DIRECTORY}/qmldir
+ PREFIX "${prefix}"
+ OUTPUT_TARGETS resource_targets
+ )
+ list(APPEND output_targets ${resource_targets})
+ # If we are adding the same file twice, we need a different resource
+ # name for the second one. It has the same QT_RESOURCE_ALIAS but a
+ # different prefix, so we can't put it in the same resource.
+ string(APPEND qmldir_resource_name "_copy")
+ endforeach()
endif()
- if(arg_PLUGIN_TARGET AND NOT arg_NO_CREATE_PLUGIN_TARGET AND NOT is_executable)
+ if(NOT arg_NO_PLUGIN AND NOT arg_NO_CREATE_PLUGIN_TARGET)
# This also handles the case where ${arg_PLUGIN_TARGET} already exists,
# including where it is the same as ${target}. If ${arg_PLUGIN_TARGET}
# already exists, it will update the necessary things that are specific
# to qml plugins.
+ if(TARGET ${arg_PLUGIN_TARGET})
+ set(plugin_lib_type "")
+ else()
+ set(plugin_lib_type ${lib_type})
+ endif()
qt6_add_qml_plugin(${arg_PLUGIN_TARGET}
- ${lib_type}
+ ${plugin_lib_type}
${no_gen_source}
OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}
BACKING_TARGET ${target}
@@ -405,13 +570,19 @@ function(qt6_add_qml_module target)
)
list(APPEND output_targets ${cache_target})
- # Build an init object library for static plugins.
+ # Build an init object library for static plugins and propagate it along with the plugin
+ # target.
+ # TODO: Figure out if we can move this code block into qt_add_qml_plugin. Need to consider
+ # various corner cases.
+ # QTBUG-96937
if(TARGET "${arg_PLUGIN_TARGET}")
- get_target_property(lib_type ${arg_PLUGIN_TARGET} TYPE)
- if(lib_type STREQUAL "STATIC_LIBRARY")
+ get_target_property(plugin_lib_type ${arg_PLUGIN_TARGET} TYPE)
+ if(plugin_lib_type STREQUAL "STATIC_LIBRARY")
__qt_internal_add_static_plugin_init_object_library(
"${arg_PLUGIN_TARGET}" plugin_init_target)
list(APPEND output_targets ${plugin_init_target})
+
+ __qt_internal_propagate_object_library("${arg_PLUGIN_TARGET}" "${plugin_init_target}")
endif()
endif()
@@ -420,9 +591,13 @@ function(qt6_add_qml_module target)
# Defer the write to allow more qml files to be added later by calls to
# qt6_target_qml_sources(). We wrap the deferred call with EVAL CODE
# so that ${target} is evaluated now rather than the end of the scope.
+ # We also delay target finalization until after our deferred write
+ # because the qmldir file must be written before any finalizer
+ # might call qt_import_qml_plugins().
cmake_language(EVAL CODE
- "cmake_language(DEFER CALL _qt_internal_write_deferred_qmldir_file ${target})"
+ "cmake_language(DEFER ID_VAR write_id CALL _qt_internal_write_deferred_qmldir_file ${target})"
)
+ _qt_internal_delay_finalization_until_after(${write_id})
else()
# Can't defer the write, have to do it now
_qt_internal_write_deferred_qmldir_file(${target})
@@ -438,9 +613,29 @@ endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
function(qt_add_qml_module)
qt6_add_qml_module(${ARGV})
+ cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT_TARGETS" "")
+ if(arg_OUTPUT_TARGETS)
+ set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE)
+ endif()
endfunction()
endif()
+# Make the prefix conform to the following:
+# - Starts with a "/"
+# - Does not end with a "/" unless the prefix is exactly "/"
+function(_qt_internal_canonicalize_resource_path path out_var)
+ if(NOT path)
+ set(path "/")
+ endif()
+ if(NOT path MATCHES "^/")
+ string(PREPEND path "/")
+ endif()
+ if(path MATCHES [[(.+)/$]])
+ set(path "${CMAKE_MATCH_1}")
+ endif()
+ set(${out_var} "${path}" PARENT_SCOPE)
+endfunction()
+
function(_qt_internal_get_escaped_uri uri out_var)
string(REGEX REPLACE "[^A-Za-z0-9]" "_" escaped_uri "${uri}")
set(${out_var} "${escaped_uri}" PARENT_SCOPE)
@@ -465,6 +660,20 @@ macro(_qt_internal_genex_getoption var target property)
set(${var} "$<BOOL:$<TARGET_PROPERTY:${target},${property}>>")
endmacro()
+function(_qt_internal_extend_qml_import_paths import_paths_var)
+ set(local_var ${${import_paths_var}})
+
+ # prepend extra import path which is a current module's build dir: we need
+ # this to ensure correct importing of QML modules when having a prefix-build
+ # with QLibraryInfo::path(QLibraryInfo::QmlImportsPath) pointing to the
+ # install location
+ if(QT_BUILDING_QT AND QT_WILL_INSTALL)
+ list(PREPEND local_var -I "${QT_BUILD_DIR}/${INSTALL_QMLDIR}")
+ endif()
+
+ set(${import_paths_var} ${local_var} PARENT_SCOPE)
+endfunction()
+
function(_qt_internal_target_enable_qmllint target)
set(lint_target ${target}_qmllint)
if(TARGET ${lint_target})
@@ -479,17 +688,47 @@ function(_qt_internal_target_enable_qmllint target)
_qt_generated_qrc_files "--resource$<SEMICOLON>" "$<SEMICOLON>"
)
- # Facilitate self-import so it can find the qmldir file
- list(APPEND import_args -I "${CMAKE_CURRENT_BINARY_DIR}")
+ # Facilitate self-import so it can find the qmldir file. We also try to walk
+ # back up the directory structure to find a base path under which this QML
+ # module is located. Such a base path is likely to be used for other QML
+ # modules that we might need to find, so add it to the import path if we
+ # find a compatible directory structure. It doesn't make sense to do this
+ # for an executable though, since it can never be found as a QML module for
+ # a different QML module/target.
+ get_target_property(target_type ${target} TYPE)
+ get_target_property(is_android_executable ${target} _qt_is_android_executable)
+ if(target_type STREQUAL "EXECUTABLE" OR is_android_executable)
+ # The executable's own QML module's qmldir file will usually be under a
+ # subdirectory (matching the module's target path) below the target's
+ # build directory.
+ list(APPEND import_args -I "$<TARGET_PROPERTY:${target},BINARY_DIR>")
+ elseif(target_type MATCHES "LIBRARY")
+ get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
+ get_target_property(target_path ${target} QT_QML_MODULE_TARGET_PATH)
+ if(output_dir MATCHES "${target_path}$")
+ string(REGEX REPLACE "(.*)/${target_path}" "\\1" base_dir "${output_dir}")
+ list(APPEND import_args -I "${base_dir}")
+ else()
+ message(WARNING
+ "The ${target} target is a QML module with target path ${target_path}. "
+ "It uses an OUTPUT_DIRECTORY of ${output_dir}, which should end in the "
+ "same target path, but doesn't. Tooling such as qmllint may not work "
+ "correctly."
+ )
+ endif()
+ endif()
if(NOT "${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "")
list(APPEND import_args -I "${QT_QML_OUTPUT_DIRECTORY}")
endif()
+ _qt_internal_extend_qml_import_paths(import_args)
+
set(cmd
${QT_TOOL_COMMAND_WRAPPER_PATH}
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
${import_args}
+ ${qrc_args}
${qmllint_files}
)
@@ -533,7 +772,7 @@ function(_qt_internal_propagate_qmlcache_object_lib
link_condition
output_generated_target)
set(resource_target "${target}_qmlcache")
- add_library("${resource_target}" OBJECT "${generated_source_code}")
+ qt6_add_library("${resource_target}" OBJECT "${generated_source_code}")
# Needed to trigger the handling of the object library for .prl generation.
set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_name ${resource_target})
@@ -600,6 +839,9 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
_qt_generated_qrc_files "--resource$<SEMICOLON>" "$<SEMICOLON>"
)
+ if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.20")
+ set(qmlcachegen "$<COMMAND_CONFIG:${qmlcachegen}>")
+ endif()
set(cmd
${QT_TOOL_COMMAND_WRAPPER_PATH}
${qmlcachegen}
@@ -624,6 +866,21 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
$<TARGET_PROPERTY:${target},_qt_generated_qrc_files>
)
+ # The current scope sees the file as generated automatically, but the
+ # target scope may not if it is different. Force it where we can.
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set_source_files_properties(
+ ${qmlcache_loader_cpp}
+ TARGET_DIRECTORY ${target}
+ PROPERTIES GENERATED TRUE
+ )
+ endif()
+ get_target_property(target_source_dir ${target} SOURCE_DIR)
+ if(NOT target_source_dir STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ add_custom_target(${target}_qmlcachegen DEPENDS ${qmlcache_loader_cpp})
+ add_dependencies(${target} ${target}_qmlcachegen)
+ endif()
+
# TODO: Probably need to reject ${target} being an object library as unsupported
get_target_property(target_type ${target} TYPE)
if(target_type STREQUAL "STATIC_LIBRARY")
@@ -678,6 +935,8 @@ function(_qt_internal_target_generate_qmldir target)
endif()
set(content "module ${uri}\n")
+ _qt_internal_qmldir_item(linktarget QT_QML_MODULE_INSTALLED_PLUGIN_TARGET)
+
get_target_property(plugin_target ${target} QT_QML_MODULE_PLUGIN_TARGET)
if(plugin_target)
get_target_property(no_plugin_optional ${target} QT_QML_MODULE_NO_PLUGIN_OPTIONAL)
@@ -685,8 +944,13 @@ function(_qt_internal_target_generate_qmldir target)
string(APPEND content "optional ")
endif()
- get_target_property(qt_libinfix ${target} QT_QML_MODULE_LIBINFIX)
- string(APPEND content "plugin ${plugin_target}${qt_libinfix}\n")
+ get_target_property(target_path ${target} QT_QML_MODULE_TARGET_PATH)
+ _qt_internal_get_qml_plugin_output_name(plugin_output_name ${plugin_target}
+ TARGET_PATH "${target_path}"
+ URI "${uri}"
+ )
+ string(APPEND content "plugin ${plugin_output_name}\n")
+
_qt_internal_qmldir_item(classname QT_QML_MODULE_CLASS_NAME)
endif()
@@ -695,7 +959,7 @@ function(_qt_internal_target_generate_qmldir target)
string(APPEND content "designersupported\n")
endif()
- _qt_internal_qmldir_item(typeinfo QT_QMLTYPES_FILENAME)
+ _qt_internal_qmldir_item(typeinfo QT_QML_MODULE_TYPEINFO)
_qt_internal_qmldir_item_list(import QT_QML_MODULE_IMPORTS)
_qt_internal_qmldir_item_list("optional import" QT_QML_MODULE_OPTIONAL_IMPORTS)
@@ -722,61 +986,14 @@ function(_qt_internal_target_generate_qmldir target)
# NOTE: qt6_target_qml_sources() may append further content later.
endfunction()
-# TODO: Need to consider the case where an executable's finalizer might execute
-# before our deferred call. That can occur in the following situations:
-#
-# - The executable target is created in the same scope as the qml module
-# and the executable target is created first.
-# - The qml module is created in a parent scope of the executable.
-#
-# Note that the qml module can safely be created in another scope as long
-# as that scope has been finalized by the time the executable target's
-# finalizer is called. A child scope satisfies this, as does any other
-# scope that has already finished being processed earlier in the CMake run.
function(_qt_internal_write_deferred_qmldir_file target)
get_target_property(__qt_qmldir_content ${target} _qt_internal_qmldir_content)
- get_target_property(out_dir ${target} QT_QML_MODULE_OUTPUT_DIR)
+ get_target_property(out_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
set(qmldir_file "${out_dir}/qmldir")
configure_file(${__qt_qml_macros_module_base_dir}/Qt6qmldirTemplate.cmake.in ${qmldir_file} @ONLY)
endfunction()
-# Create a Qml plugin. Projects should not normally need to call this function
-# directly. Rather, it would normally be called by qt6_add_qml_module() to
-# create or update the plugin associated with its backing target.
-#
-# target: The name of the target to use for the qml plugin. If it does not
-# already exist, it will be created. (REQUIRED)
-#
-# STATIC, SHARED: Explicitly specify the type of plugin library to create.
-# At most one of these two options can be specified. (OPTIONAL)
-#
-# BACKING_TARGET: The backing target that the plugin is associated with. This
-# can be the same as ${target}, in which case there is only the one merged
-# target. If this option is not provided, then URI must be given. (OPTIONAL)
-#
-# URI: Declares the module identifier of the qml module this plugin is
-# associated with. The module identifier is the (dotted URI notation)
-# identifier for the qml module. If URI is not given, then a BACKING_TARGET
-# must be provided and the backing target must have its URI recorded on it
-# (typically by an earlier call to qt6_add_qml_module()). (OPTIONAL)
-#
-# CLASS_NAME: By default, the class name will be taken from the backing target,
-# if provided, or falling back to the URI with "Plugin" appended. Any
-# non-alphanumeric characters in the URI will be replaced with underscores.
-# (OPTIONAL)
-#
-# OUTPUT_DIRECTORY: Specifies the directory where the plugin library will be
-# created. When not given, the output directory will be obtained from the
-# BACKING_TARGET if one has been provided. If an output directory cannot be
-# obtained from there either, the standard default as provided by CMake
-# (${CMAKE_CURRENT_BINARY_DIR}) will be used. (OPTIONAL)
-#
-# NO_GENERATE_PLUGIN_SOURCE: A .cpp file will be created for the plugin class
-# by default and automatically added to the plugin target. Use this option to
-# indicate that no such .cpp file should be generated. The caller is then
-# responsible for providing their own plugin class. (OPTIONAL)
-#
function(qt6_add_qml_plugin target)
set(args_option
STATIC
@@ -844,6 +1061,8 @@ function(qt6_add_qml_plugin target)
endif()
if(TARGET ${target})
+ # Plugin target already exists. Perform a few sanity checks, but we
+ # otherwise trust that the target is appropriate for use as a plugin.
get_target_property(target_type ${target} TYPE)
if(target_type STREQUAL "EXECUTABLE")
message(FATAL_ERROR "Plugins cannot be executables (target: ${target})")
@@ -857,10 +1076,19 @@ function(qt6_add_qml_plugin target)
endforeach()
get_target_property(existing_class_name ${target} QT_PLUGIN_CLASS_NAME)
- if(existing_class_name AND NOT existing_class_name STREQUAL arg_CLASS_NAME)
+ if(existing_class_name)
+ if(NOT existing_class_name STREQUAL arg_CLASS_NAME)
+ message(FATAL_ERROR
+ "An existing plugin target was given, but it has a different class name "
+ "(${existing_class_name}) to that being used here (${arg_CLASS_NAME})"
+ )
+ endif()
+ elseif(arg_CLASS_NAME)
+ set_property(TARGET ${target} PROPERTY QT_PLUGIN_CLASS_NAME "${arg_CLASS_NAME}")
+ else()
message(FATAL_ERROR
- "An existing target was given, but it has a different class name "
- "(${existing_class_name}) to that being used here (${arg_CLASS_NAME})"
+ "An existing '${target}' plugin target was given, but it has no class name set "
+ "and no new class name was provided."
)
endif()
else()
@@ -876,8 +1104,42 @@ function(qt6_add_qml_plugin target)
set(lib_type SHARED)
endif()
+ if(TARGET "${arg_BACKING_TARGET}")
+ # Ensure that the plugin type we create will be compatible with the
+ # type of backing target we were given
+ get_target_property(backing_type ${arg_BACKING_TARGET} TYPE)
+ if(backing_type STREQUAL "STATIC_LIBRARY")
+ if(lib_type STREQUAL "")
+ set(lib_type STATIC)
+ elseif(lib_type STREQUAL "SHARED")
+ message(FATAL_ERROR
+ "Mixing a static backing library with a non-static plugin "
+ "is not supported"
+ )
+ endif()
+ elseif(backing_type STREQUAL "SHARED_LIBRARY")
+ if(lib_type STREQUAL "")
+ set(lib_type SHARED)
+ elseif(lib_type STREQUAL "STATIC")
+ message(FATAL_ERROR
+ "Mixing a non-static backing library with a static plugin "
+ "is not supported"
+ )
+ endif()
+ elseif(backing_type STREQUAL "EXECUTABLE")
+ message(FATAL_ERROR
+ "A separate plugin should not be needed when the backing target "
+ "is an executable. Pre-create the plugin target before calling "
+ "this command if you really must have a separate plugin."
+ )
+ else()
+ # Object libraries, utility/custom targets
+ message(FATAL_ERROR "Unsupported backing target type: ${backing_type}")
+ endif()
+ endif()
+
qt6_add_plugin(${target} ${lib_type}
- TYPE qml_plugin
+ PLUGIN_TYPE qml_plugin
CLASS_NAME ${arg_CLASS_NAME}
)
endif()
@@ -900,36 +1162,21 @@ function(qt6_add_qml_plugin target)
)
endif()
- if (ANDROID)
- # Adjust Qml plugin names on Android similar to qml_plugin.prf which calls
- # $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/").
- # Example plugin names:
- # qtdeclarative
- # TARGET_PATH: QtQml/Models
- # file name: libqml_QtQml_Models_modelsplugin_arm64-v8a.so
- # qtquickcontrols2
- # TARGET_PATH: QtQuick/Controls.2/Material
- # file name:
- # libqml_QtQuick_Controls.2_Material_qtquickcontrols2materialstyleplugin_arm64-v8a.so
- if(NOT arg_TARGET_PATH AND TARGET "${arg_BACKING_TARGET}")
- get_target_property(arg_TARGET_PATH ${arg_BACKING_TARGET} QT_QML_MODULE_TARGET_PATH)
- endif()
- if(arg_TARGET_PATH)
- string(REPLACE "/" "_" android_plugin_name_infix_name "${arg_TARGET_PATH}")
- else()
- string(REPLACE "." "_" android_plugin_name_infix_name "${arg_URI}")
- endif()
-
- set(final_android_qml_plugin_name "qml_${android_plugin_name_infix_name}_${target}")
+ if(ANDROID)
+ _qt_internal_get_qml_plugin_output_name(plugin_output_name ${target}
+ BACKING_TARGET "${arg_BACKING_TARGET}"
+ TARGET_PATH "${arg_TARGET_PATH}"
+ URI "${arg_URI}"
+ )
set_target_properties(${target}
PROPERTIES
- LIBRARY_OUTPUT_NAME "${final_android_qml_plugin_name}"
+ LIBRARY_OUTPUT_NAME "${plugin_output_name}"
)
qt6_android_apply_arch_suffix(${target})
endif()
if(NOT arg_OUTPUT_DIRECTORY AND arg_BACKING_TARGET AND TARGET ${arg_BACKING_TARGET})
- get_target_property(arg_OUTPUT_DIRECTORY ${arg_BACKING_TARGET} QT_QML_OUTPUT_DIRECTORY)
+ get_target_property(arg_OUTPUT_DIRECTORY ${arg_BACKING_TARGET} QT_QML_MODULE_OUTPUT_DIRECTORY)
endif()
if(arg_OUTPUT_DIRECTORY)
# Plugin target must be in the output directory. The backing target,
@@ -948,7 +1195,7 @@ function(qt6_add_qml_plugin target)
# These are all substituted in the template file used further below
set(qt_qml_plugin_class_name "${arg_CLASS_NAME}")
set(qt_qml_plugin_moc_include_name "${generated_cpp_file_name_base}.moc")
- set(qt_qml_plugin_intro "extern void ${register_types_function_name}();")
+ set(qt_qml_plugin_intro "extern void ${register_types_function_name}();\nQ_GHS_KEEP_REFERENCE(${register_types_function_name});")
set(qt_qml_plugin_outro "")
if(QT_BUILDING_QT)
string(APPEND qt_qml_plugin_intro "\n\nQT_BEGIN_NAMESPACE")
@@ -977,10 +1224,24 @@ function(qt6_add_qml_plugin target)
endif()
target_link_libraries(${target} PRIVATE ${QT_CMAKE_EXPORT_NAMESPACE}::Qml)
- if(NOT "${arg_BACKING_TARGET}" STREQUAL target)
+
+ # Link plugin against its backing lib if it has one.
+ if(NOT arg_BACKING_TARGET STREQUAL "" AND NOT arg_BACKING_TARGET STREQUAL target)
target_link_libraries(${target} PRIVATE ${arg_BACKING_TARGET})
endif()
+ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0")
+ # Defer the collection of plugin dependencies until after any extra target_link_libraries
+ # calls that a user project might do.
+ # We wrap the deferred call with EVAL CODE
+ # so that ${target} is evaluated now rather than the end of the scope.
+ cmake_language(EVAL CODE
+ "cmake_language(DEFER CALL _qt_internal_add_static_qml_plugin_dependencies \"${target}\" \"${arg_BACKING_TARGET}\")"
+ )
+ else()
+ # Can't defer, have to do it now.
+ _qt_internal_add_static_qml_plugin_dependencies("${target}" "${arg_BACKING_TARGET}")
+ endif()
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
@@ -989,78 +1250,15 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
-# Add Qml files (.qml,.js,.mjs) to a Qml module.
-#
-# target: The backing target of the qml module. (REQUIRED)
-#
-# QML_FILES: The qml files to add to the backing target. Supported file extensions
-# are .qml, .js and .mjs. No other file types should be listed. (REQUIRED)
-#
-# RESOURCES: Resources used in QML, for example images. (OPTIONAL)
-#
-# PREFIX: The resource path under which to add the compiled qml files. If not
-# specified, the QT_QML_MODULE_RESOURCE_PREFIX property of the target is used
-# as the default, if set (that property is set by qt6_add_qml_module()).
-# If the default is empty, this option must be provided. (OPTIONAL)
-#
-# OUTPUT_TARGETS: In static builds, additional CMake targets can be created
-# which consumers of the module will need to potentially install.
-# Supply the name of an output variable, which will be set to a list of these
-# targets. If installing the main target, you will also need to install these
-# output targets for static builds. (OPTIONAL)
-#
-# NO_LINT: Do not add the specified files to the ${target}_qmllint target.
-# If this option is not given, the default will be taken from the target.
-#
-# NO_CACHEGEN: Do not compile the qml files. Add the raw qml files to the
-# target resources instead. If this option is not given, the default will be
-# taken from the target.
-#
-# NO_QMLDIR_TYPES: Do not append type information from the qml files to the
-# qmldir file associated with the qml module. If this option is not given,
-# the default will be taken from the target.
-#
-# In addition to the above NO_... options, individual files can be explicitly
-# skipped by setting the relevant source property. These are:
-#
-# - QT_QML_SKIP_QMLLINT
-# - QT_QML_SKIP_QMLDIR_ENTRY
-# - QT_QML_SKIP_CACHEGEN
-#
-# Disabling the qmldir entry for a qml file would normally only be used for a
-# file that does not expose a public type (e.g. a private JS file).
-# If appending of type information has not been disabled for a particular qml
-# file, the following additional source properties can be specified to
-# customize the file's type details:
-#
-# QT_QML_SOURCE_VERSION: Version(s) for this qml file. If not present the module
-# version will be used.
-#
-# QT_QML_SOURCE_TYPENAME: Override the file's type name. If not present, the
-# type name will be deduced using the file's basename.
-#
-# QT_QML_SINGLETON_TYPE: Set to true if this qml file contains a singleton type.
-#
-# QT_QML_INTERNAL_TYPE: When set to true, the type specified by
-# QT_QML_SOURCE_TYPENAME will not be available to users of this module.
-#
-# e.g.:
-# set_source_files_properties(my_qml_file.qml
-# PROPERTIES
-# QT_QML_SOURCE_VERSION "2.0;6.0"
-# QT_QML_SOURCE_TYPENAME MyQmlFile
-#
-# qt6_target_qml_sources(my_qml_module
-# QML_FILES
-# my_qml_file.qml
-# )
-#
-# The above will produce the following entry in the qmldir file:
-#
-# MyQmlFile 2.0 my_qml_file.qml
-#
+
function(qt6_target_qml_sources target)
+ get_target_property(uri ${target} QT_QML_MODULE_URI)
+ get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
+ if(NOT uri OR NOT output_dir)
+ message(FATAL_ERROR "Target ${target} is not a QML module")
+ endif()
+
set(args_option
NO_LINT
NO_CACHEGEN
@@ -1085,17 +1283,20 @@ function(qt6_target_qml_sources target)
message(FATAL_ERROR "Unknown/unexpected arguments: ${arg_UNPARSED_ARGUMENTS}")
endif()
- if (NOT arg_QML_FILES)
+ if (NOT arg_QML_FILES AND NOT arg_RESOURCES)
if(arg_OUTPUT_TARGETS)
set(${arg_OUTPUT_TARGETS} "" PARENT_SCOPE)
endif()
return()
endif()
- if(arg___QT_INTERNAL_FORCE_DEFER_QMLDIR OR ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0")
- set(can_defer_qmldir TRUE)
- else()
- set(can_defer_qmldir FALSE)
+ if(NOT arg___QT_INTERNAL_FORCE_DEFER_QMLDIR AND ${CMAKE_VERSION} VERSION_LESS "3.19.0")
+ message(FATAL_ERROR
+ "You are using CMake ${CMAKE_VERSION}, but CMake 3.19 or later "
+ "is required to add qml files with this function. Either pass "
+ "the qml files to qt6_add_qml_module() instead or update to "
+ "CMake 3.19 or later."
+ )
endif()
get_target_property(no_lint ${target} QT_QML_MODULE_NO_LINT)
@@ -1103,7 +1304,7 @@ function(qt6_target_qml_sources target)
get_target_property(no_qmldir ${target} QT_QML_MODULE_NO_GENERATE_QMLDIR)
get_target_property(resource_prefix ${target} QT_QML_MODULE_RESOURCE_PREFIX)
get_target_property(qml_module_version ${target} QT_QML_MODULE_VERSION)
- get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIR)
+ get_target_property(past_major_versions ${target} QT_QML_MODULE_PAST_MAJOR_VERSIONS)
if(NOT output_dir)
# Probably not a qml module. We still want to support tooling for this
@@ -1123,10 +1324,24 @@ function(qt6_target_qml_sources target)
)
endif()
endif()
- if(NOT arg_PREFIX MATCHES [[/$]])
+ _qt_internal_canonicalize_resource_path("${arg_PREFIX}" arg_PREFIX)
+ if(NOT arg_PREFIX STREQUAL "/")
string(APPEND arg_PREFIX "/")
endif()
+ if (qml_module_version MATCHES "^([0-9]+)\\.")
+ set(qml_module_files_versions "${CMAKE_MATCH_1}.0")
+ else()
+ message(FATAL_ERROR
+ "No major version found in '${qml_module_version}'."
+ )
+ endif()
+ if (past_major_versions OR past_major_versions STREQUAL "0")
+ foreach (past_major_version ${past_major_versions})
+ list(APPEND qml_module_files_versions "${past_major_version}.0")
+ endforeach()
+ endif()
+
# Linting and cachegen can still occur for a target that isn't a qml module,
# but for such targets, there is no qmldir file to update.
if(arg_NO_LINT)
@@ -1139,7 +1354,7 @@ function(qt6_target_qml_sources target)
set(no_qmldir TRUE)
endif()
- if(NOT no_cachegen)
+ if(NOT no_cachegen AND arg_QML_FILES)
_qt_internal_genex_getproperty(types_file ${target} QT_QML_MODULE_PLUGIN_TYPES_FILE)
_qt_internal_genex_getproperty(qmlcachegen ${target} QT_QMLCACHEGEN_BINARY)
_qt_internal_genex_getproperty(direct_calls ${target} QT_QMLCACHEGEN_DIRECT_CALLS)
@@ -1152,8 +1367,15 @@ function(qt6_target_qml_sources target)
_qt_internal_genex_getjoinedproperty(qrc_resource_args ${target}
_qt_generated_qrc_files "--resource$<SEMICOLON>" "$<SEMICOLON>"
)
+ get_target_property(target_type ${target} TYPE)
+ get_target_property(is_android_executable ${target} _qt_is_android_executable)
+ if(target_type STREQUAL "EXECUTABLE" OR is_android_executable)
+ # The application binary directory is part of the default import path.
+ list(APPEND import_paths -I "$<TARGET_PROPERTY:${target},BINARY_DIR>")
+ endif()
+ _qt_internal_extend_qml_import_paths(import_paths)
set(cachegen_args
- "$<${have_import_paths}:${import_paths}>"
+ ${import_paths}
"$<${have_types_file}:-i$<SEMICOLON>${types_file}>"
"$<${have_direct_calls}:--direct-calls>"
"$<${have_arguments}:${arguments}>"
@@ -1183,6 +1405,7 @@ function(qt6_target_qml_sources target)
set(non_qml_files)
set(output_targets)
+ set(copied_files)
foreach(file_src IN LISTS arg_QML_FILES arg_RESOURCES)
# We need to copy the file to the build directory now so that when
@@ -1212,12 +1435,14 @@ function(qt6_target_qml_sources target)
add_custom_command(OUTPUT ${file_out}
COMMAND ${CMAKE_COMMAND} -E copy ${file_src} ${file_out}
- DEPENDS ${file_src}
+ DEPENDS ${file_absolute}
WORKING_DIRECTORY $<TARGET_PROPERTY:${target},SOURCE_DIR>
)
+ list(APPEND copied_files ${file_out})
endif()
endforeach()
+ set(generated_sources_other_scope)
foreach(qml_file_src IN LISTS arg_QML_FILES)
# This is to facilitate updating code that used the earlier tech preview
# API function qt6_target_qml_files()
@@ -1226,9 +1451,13 @@ function(qt6_target_qml_sources target)
continue()
endif()
+ # Mark QML files as source files, so that they do not appear in <Other Locations> in Creator
+ # or other IDEs
+ set_source_files_properties(${qml_file_src} HEADER_FILE_ONLY ON)
+ target_sources(${target} PRIVATE ${qml_file_src})
+
get_filename_component(file_absolute ${qml_file_src} ABSOLUTE)
__qt_get_relative_resource_path_for_file(file_resource_path ${qml_file_src})
- set(qml_file_out ${output_dir}/${file_resource_path})
# For the tooling steps below, run the tools on the copied qml file in
# the build directory, not the source directory. This is required
@@ -1238,17 +1467,13 @@ function(qt6_target_qml_sources target)
# resource paths and the source locations might be structured quite
# differently.
- # Fed to qmlimportscanner in qt6_import_qml_plugins. Also may be used in
- # generator expressions to install all qml files for the target.
- set_property(TARGET ${target} APPEND PROPERTY QT_QML_MODULE_FILES ${qml_file_out})
-
# Add file to those processed by qmllint
get_source_file_property(skip_qmllint ${qml_file_src} QT_QML_SKIP_QMLLINT)
if(NOT no_lint AND NOT skip_qmllint)
# The set of qml files to run qmllint on may be a subset of the
# full set of files, so record these in a separate property.
_qt_internal_target_enable_qmllint(${target})
- set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${qml_file_src})
+ set_property(TARGET ${target} APPEND PROPERTY QT_QML_LINT_FILES ${file_absolute})
endif()
# Add qml file's type to qmldir
@@ -1256,27 +1481,45 @@ function(qt6_target_qml_sources target)
if(NOT no_qmldir AND NOT skip_qmldir)
get_source_file_property(qml_file_typename ${qml_file_src} QT_QML_SOURCE_TYPENAME)
if (NOT qml_file_typename)
- get_filename_component(qml_file_typename ${qml_file_src} NAME_WLE)
+ get_filename_component(qml_file_ext ${qml_file_src} EXT)
+ get_filename_component(qml_file_typename ${qml_file_src} NAME_WE)
endif()
# Do not add qmldir entries for lowercase names. Those are not components.
- if (qml_file_typename MATCHES "^[A-Z]")
- if(NOT can_defer_qmldir)
- message(FATAL_ERROR
- "You are using CMake ${CMAKE_VERSION}, but CMake 3.19 or later "
- "is required to add qml files with this function. Either pass "
- "the qml files to qt6_add_qml_module() instead or update to "
- "CMake 3.19 or later."
+ if (qml_file_typename AND qml_file_typename MATCHES "^[A-Z]")
+ if (qml_file_ext AND NOT qml_file_ext STREQUAL ".qml" AND NOT qml_file_ext STREQUAL ".ui.qml"
+ AND NOT qml_file_ext STREQUAL ".js" AND NOT qml_file_ext STREQUAL ".mjs")
+ message(AUTHOR_WARNING
+ "${qml_file_src} has a file extension different from .qml, .ui.qml, .js, "
+ "and .mjs. This leads to unexpected component names."
+ )
+ endif()
+
+ # We previously accepted the singular form of this property name
+ # during tech preview. Issue a warning for that, but still
+ # honor it. The plural form will override it if both are set.
+ get_property(have_singular_property SOURCE ${qml_file_src}
+ PROPERTY QT_QML_SOURCE_VERSION SET
+ )
+ if(have_singular_property)
+ message(AUTHOR_WARNING
+ "The QT_QML_SOURCE_VERSION source file property has been replaced "
+ "by QT_QML_SOURCE_VERSIONS (i.e. plural rather than singular). "
+ "The singular form will eventually be removed, please update "
+ "the project to use the plural form instead for the file at:\n"
+ " ${qml_file_src}"
)
endif()
+ get_source_file_property(qml_file_versions ${qml_file_src} QT_QML_SOURCE_VERSIONS)
+ if(NOT qml_file_versions AND have_singular_property)
+ get_source_file_property(qml_file_versions ${qml_file_src} QT_QML_SOURCE_VERSION)
+ endif()
- # TODO: rename to QT_QML_SOURCE_VERSIONS
- get_source_file_property(qml_file_versions ${qml_file_src} QT_QML_SOURCE_VERSION)
get_source_file_property(qml_file_singleton ${qml_file_src} QT_QML_SINGLETON_TYPE)
get_source_file_property(qml_file_internal ${qml_file_src} QT_QML_INTERNAL_TYPE)
if (NOT qml_file_versions)
- set(qml_file_versions ${qml_module_version})
+ set(qml_file_versions ${qml_module_files_versions})
endif()
set(qmldir_file_contents "")
@@ -1325,34 +1568,57 @@ function(qt6_target_qml_sources target)
"${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/${target}_${compiled_file}.cpp")
get_filename_component(out_dir ${compiled_file} DIRECTORY)
+ if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.20")
+ set(qmlcachegen_cmd "$<COMMAND_CONFIG:${qmlcachegen}>")
+ else()
+ set(qmlcachegen_cmd "${qmlcachegen}")
+ endif()
add_custom_command(
OUTPUT ${compiled_file}
COMMAND ${CMAKE_COMMAND} -E make_directory ${out_dir}
COMMAND
${QT_TOOL_COMMAND_WRAPPER_PATH}
- ${qmlcachegen}
+ ${qmlcachegen_cmd}
--resource-path "${file_resource_path}"
${cachegen_args}
-o "${compiled_file}"
"${file_absolute}"
COMMAND_EXPAND_LISTS
DEPENDS
- ${qmlcachegen}
+ ${qmlcachegen_cmd}
"${file_absolute}"
$<TARGET_PROPERTY:${target},_qt_generated_qrc_files>
"$<$<BOOL:${types_file}>:${types_file}>"
)
- target_sources(${target} PRIVATE
- ${compiled_file}
- ${qml_file_src} # Make the original qml file show up under this target in the IDE
- )
+ target_sources(${target} PRIVATE ${compiled_file})
set_source_files_properties(${compiled_file} PROPERTIES
SKIP_AUTOGEN ON
)
+ # The current scope automatically sees the file as generated, but the
+ # target scope may not if it is different. Force it where we can.
+ # We will also have to add the generated file to a target in this
+ # scope at the end to ensure correct dependencies.
+ get_target_property(target_source_dir ${target} SOURCE_DIR)
+ if(NOT target_source_dir STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ list(APPEND generated_sources_other_scope ${compiled_file})
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set_source_files_properties(
+ ${compiled_file}
+ TARGET_DIRECTORY ${target}
+ PROPERTIES
+ SKIP_AUTOGEN TRUE
+ GENERATED TRUE
+ )
+ endif()
+ endif()
endif()
endforeach()
+ if(ANDROID)
+ _qt_internal_collect_qml_root_paths("${target}" ${arg_QML_FILES})
+ endif()
+
if(non_qml_files)
list(JOIN non_qml_files "\n " file_list)
message(WARNING
@@ -1362,6 +1628,36 @@ function(qt6_target_qml_sources target)
)
endif()
+ if(copied_files OR generated_sources_other_scope)
+ if(CMAKE_VERSION VERSION_LESS 3.19)
+ # Called from qt6_add_qml_module() and we know there can only be
+ # this one call. With those constraints, we can use a custom target
+ # to implement the necessary dependencies to get files copied to the
+ # build directory when their source files change.
+ add_custom_target(${target}_tooling ALL
+ DEPENDS
+ ${copied_files}
+ ${generated_sources_other_scope}
+ )
+ add_dependencies(${target} ${target}_tooling)
+ else()
+ # We could be called multiple times and a custom target can only
+ # have file-level dependencies added at the time the target is
+ # created. Use an interface library instead, since we can add
+ # private sources to those and have the library act as a build
+ # system target from CMake 3.19 onward, and we can add the sources
+ # progressively over multiple calls.
+ if(NOT TARGET ${target}_tooling)
+ add_library(${target}_tooling INTERFACE)
+ add_dependencies(${target} ${target}_tooling)
+ endif()
+ target_sources(${target}_tooling PRIVATE
+ ${copied_files}
+ ${generated_sources_other_scope}
+ )
+ endif()
+ endif()
+
# Batch all the non-compiled qml sources into a single resource for this
# call. Subsequent calls for the same target will be in their own separate
# resource file.
@@ -1380,7 +1676,7 @@ function(qt6_target_qml_sources target)
set_target_properties(${target} PROPERTIES QT_QML_MODULE_RAW_QML_SETS ${counter})
list(APPEND output_targets ${resource_targets})
- if(arg_OUTPUT_TARGETS AND output_targets)
+ if(arg_OUTPUT_TARGETS)
set(${arg_OUTPUT_TARGETS} ${output_targets} PARENT_SCOPE)
endif()
@@ -1389,13 +1685,13 @@ endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
function(qt_target_qml_sources)
qt6_target_qml_sources(${ARGV})
+ cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT_TARGETS" "")
+ if(arg_OUTPUT_TARGETS)
+ set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE)
+ endif()
endfunction()
endif()
-# NOTE: This function does not normally need to be called directly by projects.
-# It is called automatically by qt6_add_qml_module() unless
-# NO_GENERATE_QMLTYPES is also given to that function.
-#
# target: Expected to be the backing target for a qml module. Certain target
# properties normally set by qt6_add_qml_module() will be retrieved from this
# target. (REQUIRED)
@@ -1403,8 +1699,8 @@ endif()
# MANUAL_MOC_JSON_FILES: Specifies a list of json files, generated by a manual
# moc call, to extract metatypes. (OPTIONAL)
#
-function(qt6_qml_type_registration target)
- set(args_option "")
+function(_qt_internal_qml_type_registration target)
+ set(args_option __QT_INTERNAL_INSTALL_METATYPES_JSON)
set(args_single "")
set(args_multi MANUAL_MOC_JSON_FILES)
@@ -1419,7 +1715,7 @@ function(qt6_qml_type_registration target)
if (NOT import_name)
message(FATAL_ERROR "Target ${target} is not a QML module")
endif()
- get_target_property(qmltypes_output_name ${target} QT_QMLTYPES_FILENAME)
+ get_target_property(qmltypes_output_name ${target} QT_QML_MODULE_TYPEINFO)
if (NOT qmltypes_output_name)
get_target_property(compile_definitions_list ${target} COMPILE_DEFINITIONS)
list(FIND compile_definitions_list QT_PLUGIN is_a_plugin)
@@ -1430,25 +1726,20 @@ function(qt6_qml_type_registration target)
endif()
endif()
- # Horrible hack workaround to not install metatypes.json files for examples/ qml plugins into
- # ${qt_prefix}/lib/meta_types.
- # Put them into QT_QML_MODULE_INSTALL_DIR/lib/meta_types instead.
- get_target_property(qml_install_dir ${target} QT_QML_MODULE_INSTALL_DIR)
set(meta_types_json_args "")
-
- # TODO: This is internal Qt logic, it should be moved out of here.
- if(QT_BUILDING_QT AND QT_WILL_INSTALL AND qml_install_dir AND
- qml_install_dir MATCHES "^${INSTALL_EXAMPLESDIR}")
- set(meta_types_json_args "INSTALL_DIR" "${qml_install_dir}/lib/metatypes")
- endif()
-
if(arg_MANUAL_MOC_JSON_FILES)
list(APPEND meta_types_json_args "MANUAL_MOC_JSON_FILES" ${arg_MANUAL_MOC_JSON_FILES})
endif()
+
+ # Don't install the metatypes json files by default for user project created qml modules.
+ # Only install them for Qt provided qml modules.
+ if(NOT arg___QT_INTERNAL_INSTALL_METATYPES_JSON)
+ list(APPEND meta_types_json_args __QT_INTERNAL_NO_INSTALL)
+ endif()
qt6_extract_metatypes(${target} ${meta_types_json_args})
get_target_property(import_version ${target} QT_QML_MODULE_VERSION)
- get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIR)
+ get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
get_target_property(target_source_dir ${target} SOURCE_DIR)
get_target_property(target_binary_dir ${target} BINARY_DIR)
get_target_property(target_metatypes_file ${target} INTERFACE_QT_META_TYPES_BUILD_FILE)
@@ -1489,7 +1780,7 @@ function(qt6_qml_type_registration target)
)
# Add past minor versions
- get_target_property(past_major_versions ${target} QT_QML_PAST_MAJOR_VERSIONS)
+ get_target_property(past_major_versions ${target} QT_QML_MODULE_PAST_MAJOR_VERSIONS)
if (past_major_versions OR past_major_versions STREQUAL "0")
foreach (past_major_version ${past_major_versions})
@@ -1519,6 +1810,11 @@ function(qt6_qml_type_registration target)
if (TARGET ${target}Private)
list(APPEND cmd_args --private-includes)
+ else()
+ string(REGEX MATCH "Private$" privateSuffix ${target})
+ if (privateSuffix)
+ list(APPEND cmd_args --private-includes)
+ endif()
endif()
get_target_property(target_metatypes_json_file ${target} INTERFACE_QT_META_TYPES_BUILD_FILE)
@@ -1591,6 +1887,19 @@ function(qt6_qml_type_registration target)
endif()
add_dependencies(all_qmltyperegistrations ${target}_qmltyperegistration)
+ # Both ${target} (via target_sources) and ${target}_qmltyperegistration (via add_custom_target
+ # DEPENDS option) depend on ${type_registration_cpp_file}.
+ # The new Xcode build system requires a common target to drive the generation of files,
+ # otherwise project configuration fails.
+ # Make ${target} the common target, by adding it as a dependency for
+ # ${target}_qmltyperegistration.
+ # The consequence is that the ${target}_qmllint target will now first build ${target} when using
+ # the Xcode generator (mostly only relevant for projects using Qt for iOS).
+ # See QTBUG-95763.
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ add_dependencies(${target}_qmltyperegistration ${target})
+ endif()
+
target_sources(${target} PRIVATE ${type_registration_cpp_file})
# FIXME: The generated .cpp file has usually lost the path information for
@@ -1614,15 +1923,35 @@ function(qt6_qml_type_registration target)
SKIP_AUTOGEN ON
${additional_source_files_properties}
)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set_source_files_properties(
+ ${type_registration_cpp_file}
+ TARGET_DIRECTORY ${target}
+ PROPERTIES
+ SKIP_AUTOGEN TRUE
+ GENERATED TRUE
+ ${additional_source_files_properties}
+ )
+ endif()
target_include_directories(${target} PRIVATE
$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::QmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
)
endfunction()
+function(qt6_qml_type_registration)
+ message(FATAL_ERROR
+ "This function, previously available under Technical Preview, has been removed. "
+ "Please use qt6_add_qml_module() instead."
+ )
+endfunction()
+
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
function(qt_qml_type_registration)
- qt6_qml_type_registration(${ARGV})
+ message(FATAL_ERROR
+ "This function, previously available under Technical Preview, has been removed. "
+ "Please use qt_add_qml_module() instead."
+ )
endfunction()
endif()
@@ -1643,7 +1972,7 @@ function(qt6_import_qml_plugins target)
set_target_properties(${target} PROPERTIES _QT_QML_PLUGINS_IMPORTED TRUE)
set(options)
- set(oneValueArgs "PATH_TO_SCAN")
+ set(oneValueArgs "PATH_TO_SCAN") # Internal option, may be removed
set(multiValueArgs)
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
@@ -1676,10 +2005,24 @@ but this file does not exist. Possible reasons include:
")
endif()
- # Find location of qml dir.
- # TODO: qt.prf implies that there might be more than one qml import path to
- # pass to qmlimportscanner.
- set(qml_path "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_QML}")
+ # Find Qt provided QML import paths.
+ if("${_qt_additional_packages_prefix_paths}" STREQUAL "")
+ # We have one installation prefix for all Qt modules. Add the "<prefix>/qml" directory.
+ set(qml_import_paths "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_QML}")
+ else()
+ # We have multiple installation prefixes: one per Qt repository (conan). Add those that have
+ # a "qml" subdirectory.
+ set(qml_import_paths)
+ __qt_internal_prefix_paths_to_roots(
+ additional_root_paths "${_qt_additional_packages_prefix_paths}")
+ foreach(root IN ITEMS ${QT6_INSTALL_PREFIX} ${additional_root_paths})
+ set(candidate "${root}/${QT6_INSTALL_QML}")
+ if(IS_DIRECTORY "${candidate}")
+ list(APPEND qml_import_paths "${candidate}")
+ endif()
+ endforeach()
+ endif()
+
# Small macro to avoid duplicating code in two different loops.
macro(_qt6_QmlImportScanner_parse_entry)
@@ -1688,7 +2031,8 @@ but this file does not exist. Possible reasons include:
set(entry_name "qml_import_scanner_import_${idx}")
cmake_parse_arguments("entry"
""
- "CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;" ""
+ "CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;LINKTARGET"
+ ""
${${entry_name}}
)
endmacro()
@@ -1700,28 +2044,41 @@ but this file does not exist. Possible reasons include:
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.qt_plugins)
set(cmd_args
- "${arg_PATH_TO_SCAN}"
+ -rootPath "${arg_PATH_TO_SCAN}"
-cmake-output
- -importPath "${qml_path}"
)
get_target_property(qml_import_path ${target} QT_QML_IMPORT_PATH)
-
- if (qml_import_path)
- list(APPEND cmd_args ${qml_import_path})
+ if(qml_import_path)
+ list(APPEND qml_import_paths ${qml_import_path})
endif()
# Facilitate self-import so we can find the qmldir file
- list(APPEND cmd_args "${CMAKE_CURRENT_BINARY_DIR}")
-
- if(NOT "${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "" AND EXISTS "${QT_QML_OUTPUT_DIRECTORY}")
- list(APPEND cmd_args "${QT_QML_OUTPUT_DIRECTORY}")
+ get_target_property(module_out_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
+ if(module_out_dir)
+ list(APPEND qml_import_paths "${module_out_dir}")
endif()
- get_target_property(qml_files ${target} QT_QML_MODULE_FILES)
- if (qml_files)
- list(APPEND cmd_args "-qmlFiles" ${qml_files})
+ # Find qmldir files we copied to the build directory
+ if(NOT "${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "")
+ if(EXISTS "${QT_QML_OUTPUT_DIRECTORY}")
+ list(APPEND qml_import_paths "${QT_QML_OUTPUT_DIRECTORY}")
+ endif()
+ else()
+ list(APPEND qml_import_paths "${CMAKE_CURRENT_BINARY_DIR}")
endif()
+ # Construct the -importPath arguments.
+ set(import_path_arguments)
+ foreach(path IN LISTS qml_import_paths)
+ list(APPEND import_path_arguments -importPath ${path})
+ endforeach()
+
+ list(APPEND cmd_args ${import_path_arguments})
+
+ # All of the module's .qml files will be listed in one of the generated
+ # .qrc files, so there's no need to list the files individually. We provide
+ # the .qrc files instead because they have the additional information for
+ # each file's resource alias.
get_target_property(qrc_files ${target} _qt_generated_qrc_files)
if (qrc_files)
list(APPEND cmd_args "-qrcFiles" ${qrc_files})
@@ -1779,28 +2136,20 @@ but this file does not exist. Possible reasons include:
# (typically brought in via find_package(Qt6...) ).
# For other plugins, the targets can come from the project itself.
#
- # Handles Qt provided Qml plugins
- if(TARGET "${QT_CMAKE_EXPORT_NAMESPACE}::${entry_PLUGIN}")
- set(plugin_target "${QT_CMAKE_EXPORT_NAMESPACE}::${entry_PLUGIN}")
- list(APPEND plugins_to_link "${plugin_target}")
-
- __qt_internal_get_static_plugin_init_target_name("${entry_PLUGIN}"
- plugin_init_target)
- set(plugin_init_target_prefixed
- "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_init_target}")
- list(APPEND plugin_inits_to_link "${plugin_init_target_prefixed}")
-
- # Handles user project provided Qml plugins
- elseif(TARGET ${entry_PLUGIN} AND TARGET ${entry_PLUGIN}_init)
- set(plugin_target "${entry_PLUGIN}")
- list(APPEND plugins_to_link "${plugin_target}")
-
- __qt_internal_get_static_plugin_init_target_name("${entry_PLUGIN}"
- plugin_init_target)
- list(APPEND plugin_inits_to_link "${plugin_init_target}")
-
- # TODO: QTBUG-94605 Figure out if this is a reasonable scenario to support
+ if(entry_LINKTARGET)
+ if(TARGET ${entry_LINKTARGET})
+ list(APPEND plugins_to_link "${entry_LINKTARGET}")
+ else()
+ message(WARNING
+ "The qml plugin '${entry_PLUGIN}' is a dependency of '${target}', "
+ "but the link target it defines (${entry_LINKTARGET}) does not exist "
+ "in the current scope. The plugin will not be linked."
+ )
+ endif()
+ elseif(TARGET ${entry_PLUGIN})
+ list(APPEND plugins_to_link "${entry_PLUGIN}")
else()
+ # TODO: QTBUG-94605 Figure out if this is a reasonable scenario to support
message(WARNING
"The qml plugin '${entry_PLUGIN}' is a dependency of '${target}', "
"but there is no target by that name in the current scope. The plugin will "
@@ -1811,27 +2160,18 @@ but this file does not exist. Possible reasons include:
endforeach()
if(plugins_to_link)
- # __qt_internal_propagate_object_library propagates object libraries by setting
- # INTERFACE linkage on ${target}. If ${target} is an executable, that would mean none
- # of the plugin init libraries would be linked to the executable itself. If ${target} is
- # a user shared library, it would be similar to the case above, the plugin init
- # libraries should be linked into the shared library, rather than to the consumer of the
- # shared library.
- # To achieve proper linkage in these cases, link the plugins and initializers directly.
- # For static libraries and INTERFACE libraries,
- # using __qt_internal_propagate_object_library is intended.
+ # If ${target} is an executable or a shared library, link the plugins directly to
+ # the target.
+ # If ${target} is a static or INTERFACE library, the plugins should be propagated
+ # across those libraries to the end target (executable or shared library).
+ # The plugin initializers will be linked via usage requirements from the plugin target.
get_target_property(target_type ${target} TYPE)
if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY")
- # This links both the initializer object and the usage requirements of the object
- # library, so Qt::Core.
- target_link_libraries("${target}" PRIVATE ${plugin_inits_to_link})
- target_link_libraries("${target}" PRIVATE ${plugins_to_link})
+ set(link_type "PRIVATE")
else()
- foreach(plugin_init IN LISTS plugin_inits_to_link)
- __qt_internal_propagate_object_library("${target}" "${plugin_init}")
- endforeach()
- target_link_libraries("${target}" INTERFACE ${plugins_to_link})
+ set(link_type "INTERFACE")
endif()
+ target_link_libraries("${target}" ${link_type} ${plugins_to_link})
endif()
endif()
endfunction()
@@ -1845,3 +2185,188 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endif()
endfunction()
endif()
+
+function(_qt_internal_add_static_qml_plugin_dependencies plugin_target backing_target)
+ # Protect against multiple calls of qt_add_qml_plugin.
+ get_target_property(plugin_deps_added "${plugin_target}" _qt_extra_static_qml_plugin_deps_added)
+ if(plugin_deps_added)
+ return()
+ endif()
+ set_target_properties("${plugin_target}" PROPERTIES _qt_extra_static_qml_plugin_deps_added TRUE)
+
+ # Get the install plugin target name, which we will need for filtering later on.
+ if(TARGET "${backing_target}")
+ get_target_property(installed_plugin_target
+ "${backing_target}" _qt_qml_module_installed_plugin_target)
+ endif()
+
+ if(NOT backing_target STREQUAL plugin_target AND TARGET "${backing_target}")
+ set(has_backing_lib TRUE)
+ else()
+ set(has_backing_lib FALSE)
+ endif()
+
+ get_target_property(plugin_type ${plugin_target} TYPE)
+ set(skip_prl_marker "$<BOOL:QT_IS_PLUGIN_GENEX>")
+
+ # If ${plugin_target} is a static qml plugin, recursively get its private dependencies (and its
+ # backing lib private deps), identify which of those are qml modules, extract any associated qml
+ # plugin target from those qml modules and make them dependencies of ${plugin_target}.
+ #
+ # E.g. this ensures that if a user project links directly to the static qtquick2plugin plugin
+ # target (note the plugin target, not the backing lib) it will automatically also link to
+ # Quick's transitive plugin dependencies: qmlplugin, modelsplugin and workerscriptplugin, in
+ # addition to the the Qml, QmlModels and QmlWorkerScript backing libraries.
+ #
+ # Note this logic is not specific to qtquick2plugin, it applies to all static qml plugins.
+ #
+ # This eliminates the needed boilerplate to link to the full transitive closure of qml plugins
+ # in user projects that don't want to use qmlimportscanner / qt_import_qml_plugins.
+ set(additional_plugin_deps "")
+
+ if(plugin_type STREQUAL "STATIC_LIBRARY")
+ set(all_private_deps "")
+
+ # We walk both plugin_target and backing_lib private deps because they can have differing
+ # dependencies and we want to consider all of them.
+ __qt_internal_collect_all_target_dependencies(
+ "${plugin_target}" plugin_private_deps)
+ if(plugin_private_deps)
+ list(APPEND all_private_deps ${plugin_private_deps})
+ endif()
+
+ if(has_backing_lib)
+ __qt_internal_collect_all_target_dependencies(
+ "${backing_target}" backing_lib_private_deps)
+ if(backing_lib_private_deps)
+ list(APPEND all_private_deps ${backing_lib_private_deps})
+ endif()
+ endif()
+
+ foreach(dep IN LISTS all_private_deps)
+ if(NOT TARGET "${dep}")
+ continue()
+ endif()
+ get_target_property(dep_type ${dep} TYPE)
+ if(dep_type STREQUAL "STATIC_LIBRARY")
+ set(associated_qml_plugin "")
+
+ # Check if the target has an associated imported qml plugin (like a Qt-provided
+ # one).
+ get_target_property(associated_qml_plugin_candidate ${dep}
+ _qt_qml_module_installed_plugin_target)
+
+ if(associated_qml_plugin_candidate AND TARGET "${associated_qml_plugin_candidate}")
+ set(associated_qml_plugin "${associated_qml_plugin_candidate}")
+ endif()
+
+ # Check if the target has an associated qml plugin that's built as part of the
+ # current project (non-installed one, so without a target namespace prefix).
+ get_target_property(associated_qml_plugin_candidate ${dep}
+ _qt_qml_module_plugin_target)
+
+ if(NOT associated_qml_plugin AND
+ associated_qml_plugin_candidate
+ AND TARGET "${associated_qml_plugin_candidate}")
+ set(associated_qml_plugin "${associated_qml_plugin_candidate}")
+ endif()
+
+ # We need to filter out adding the plugin_target as a dependency to itself,
+ # when walking the backing lib of the plugin_target.
+ if(associated_qml_plugin
+ AND NOT associated_qml_plugin STREQUAL plugin_target
+ AND NOT associated_qml_plugin STREQUAL installed_plugin_target)
+ # Abuse a genex marker, to skip the dependency to be added into prl files.
+ # TODO: Introduce a more generic marker name in qtbase specifically
+ # for skipping deps in prl file deps generation.
+ set(wrapped_associated_qml_plugin
+ "$<${skip_prl_marker}:$<TARGET_NAME:${associated_qml_plugin}>>")
+
+ if(NOT wrapped_associated_qml_plugin IN_LIST additional_plugin_deps)
+ list(APPEND additional_plugin_deps "${wrapped_associated_qml_plugin}")
+ endif()
+ endif()
+ endif()
+ endforeach()
+ endif()
+
+ if(additional_plugin_deps)
+ target_link_libraries(${plugin_target} PRIVATE ${additional_plugin_deps})
+ endif()
+endfunction()
+
+# The function returns the output name of a qml plugin that will be used as library output
+# name and in a qmldir file as the 'plugin <plugin_output_name>' record.
+function(_qt_internal_get_qml_plugin_output_name out_var plugin_target)
+ cmake_parse_arguments(arg
+ ""
+ "BACKING_TARGET;TARGET_PATH;URI"
+ ""
+ ${ARGN}
+ )
+ set(plugin_name)
+ if(TARGET ${plugin_target})
+ get_target_property(plugin_name ${plugin_target} OUTPUT_NAME)
+ endif()
+ if(NOT plugin_name)
+ set(plugin_name "${plugin_target}")
+ endif()
+
+ if(ANDROID)
+ # In Android all plugins are stored in directly the /libs directory. This means that plugin
+ # names must be unique in scope of apk. To make this work we prepend uri-based prefix to
+ # each qml plugin in case if users don't use the manually written qmldir files.
+ get_target_property(no_generate_qmldir ${target} QT_QML_MODULE_NO_GENERATE_QMLDIR)
+ if(TARGET "${arg_BACKING_TARGET}")
+ get_target_property(no_generate_qmldir ${arg_BACKING_TARGET}
+ QT_QML_MODULE_NO_GENERATE_QMLDIR)
+
+ # Adjust Qml plugin names on Android similar to qml_plugin.prf which calls
+ # $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/").
+ # Example plugin names:
+ # qtdeclarative
+ # TARGET_PATH: QtQml/Models
+ # file name: libqml_QtQml_Models_modelsplugin_x86_64.so
+ # qtquickcontrols2
+ # TARGET_PATH: QtQuick/Controls.2/Material
+ # file name:
+ # libqml_QtQuick_Controls.2_Material_qtquickcontrols2materialstyleplugin_x86_64.so
+ if(NOT arg_TARGET_PATH)
+ get_target_property(arg_TARGET_PATH ${arg_BACKING_TARGET}
+ QT_QML_MODULE_TARGET_PATH)
+ endif()
+ endif()
+ if(arg_TARGET_PATH)
+ string(REPLACE "/" "_" android_plugin_name_infix_name "${arg_TARGET_PATH}")
+ else()
+ string(REPLACE "." "_" android_plugin_name_infix_name "${arg_URI}")
+ endif()
+
+ # If plugin supposed to use manually written qmldir file we don't prepend the uri-based
+ # prefix to the plugin output name. User should keep the file name of a QML plugin in
+ # qmldir the same as the name of plugin on a file system. Exception is the
+ # ABI-/platform-specific suffix that has the separate processing and should not be
+ # a part of plugin name in qmldir.
+ if(NOT no_generate_qmldir)
+ set(plugin_name
+ "qml_${android_plugin_name_infix_name}_${plugin_name}")
+ endif()
+ endif()
+
+ set(${out_var} "${plugin_name}" PARENT_SCOPE)
+endfunction()
+
+# Used to add extra dependencies between ${target} and ${dep_target} qml plugins in a static
+# Qt build, without creating a dependency in the genereated qmake .prl files.
+# These dependencies make manual linking to static plugins a nicer experience for users that don't
+# want to use qt_import_qml_plugins.
+function(_qt_internal_add_qml_static_plugin_dependency target dep_target)
+ if(NOT BUILD_SHARED_LIBS)
+ # Abuse a genex marker, to skip the dependency to be added into prl files.
+ # TODO: Introduce a more generic marker name in qtbase specifically
+ # for skipping deps in prl file deps generation.
+ set(skip_prl_marker "$<BOOL:QT_IS_PLUGIN_GENEX>")
+ target_link_libraries("${target}" PRIVATE
+ "$<${skip_prl_marker}:$<TARGET_NAME:${dep_target}>>")
+ endif()
+endfunction()
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 76e3f2b7e6..05cf786e7d 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -243,6 +243,7 @@ void QQmlAnimationTimer::registerRunningAnimation(QAbstractAnimationJob *animati
void QQmlAnimationTimer::unregisterRunningAnimation(QAbstractAnimationJob *animation)
{
+ unsetJobTimer(animation);
if (animation->userControlDisabled())
return;
@@ -307,9 +308,10 @@ QAbstractAnimationJob::~QAbstractAnimationJob()
Q_ASSERT(m_state == Stopped);
if (oldState == Running) {
- Q_ASSERT(QQmlAnimationTimer::instance(false) == m_timer);
- if (m_timer)
+ if (m_timer) {
+ Q_ASSERT(QQmlAnimationTimer::instance(false) == m_timer);
m_timer->unregisterAnimation(this);
+ }
}
Q_ASSERT(!m_hasRegisteredTimer);
}
@@ -551,6 +553,14 @@ void QAbstractAnimationJob::stop()
setState(Stopped);
}
+void QAbstractAnimationJob::complete()
+{
+ // Simulate the full animation cycle
+ setState(Running);
+ setCurrentTime(m_direction == Forward ? duration() : 0);
+ setState(Stopped);
+}
+
void QAbstractAnimationJob::pause()
{
if (m_state == Stopped) {
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 184bfe5e4f..dd978c93d0 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -114,6 +114,7 @@ public:
void pause();
void resume();
void stop();
+ void complete();
enum ChangeType {
Completion = 0x01,
diff --git a/src/qml/animations/qcontinuinganimationgroupjob.cpp b/src/qml/animations/qcontinuinganimationgroupjob.cpp
index 7d24d8ce55..6d2dc38bfd 100644
--- a/src/qml/animations/qcontinuinganimationgroupjob.cpp
+++ b/src/qml/animations/qcontinuinganimationgroupjob.cpp
@@ -81,9 +81,9 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS
return;
}
for (QAbstractAnimationJob *animation : m_children) {
- resetUncontrolledAnimationFinishTime(animation);
+ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation));
animation->setDirection(m_direction);
- animation->start();
+ RETURN_IF_DELETED(animation->start());
}
break;
}
diff --git a/src/qml/animations/qparallelanimationgroupjob.cpp b/src/qml/animations/qparallelanimationgroupjob.cpp
index 4da4a06af2..0d67b2451e 100644
--- a/src/qml/animations/qparallelanimationgroupjob.cpp
+++ b/src/qml/animations/qparallelanimationgroupjob.cpp
@@ -144,10 +144,10 @@ void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newSta
animation->stop();
m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1;
}
- resetUncontrolledAnimationFinishTime(animation);
+ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation));
animation->setDirection(m_direction);
if (shouldAnimationStart(animation, oldState == Stopped))
- animation->start();
+ RETURN_IF_DELETED(animation->start());
}
break;
}
diff --git a/src/qml/common/qqmljsfixedpoolarray_p.h b/src/qml/common/qqmljsfixedpoolarray_p.h
index b65b994d6c..15a8cd6878 100644
--- a/src/qml/common/qqmljsfixedpoolarray_p.h
+++ b/src/qml/common/qqmljsfixedpoolarray_p.h
@@ -86,7 +86,7 @@ public:
if (QTypeInfo<T>::isComplex) {
for (int i = 0; i < count; ++i)
new (data + i) T(vector.at(i));
- } else {
+ } else if (count) {
memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
}
}
diff --git a/src/qml/common/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h
index 65c3e4d65a..b42f74a67b 100644
--- a/src/qml/common/qv4alloca_p.h
+++ b/src/qml/common/qv4alloca_p.h
@@ -100,8 +100,10 @@ QT_END_NAMESPACE
type *name = nullptr
#define Q_ALLOCA_ASSIGN(type, name, size) \
- _qt_alloca_##name.allocate(size); \
- name = static_cast<type*>(_qt_alloca_##name.data())
+ do { \
+ _qt_alloca_##name.allocate(size); \
+ name = static_cast<type*>(_qt_alloca_##name.data()); \
+ } while (false)
#endif
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 00a1890bf6..8b2d5066c6 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -79,7 +79,11 @@ 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 0x33 // added new bytecode instruction for type assertions
+
+// Note: We got two different versions 0x36 now, one with builtin value types and one without.
+// However, the one without is exclusive to Qt 6.4+ and the compiled data cannot be preserved
+// across Qt versions. Therefore, this is fine.
+#define QV4_DATA_STRUCTURE_VERSION 0x36 // reordered runtime functions when compiling at run time
class QIODevice;
class QQmlTypeNameCache;
@@ -128,33 +132,45 @@ struct TableIterator
struct Location
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 20> line;
- quint32_le_bitfield<20, 12> column;
- };
-
- Location() : _dummy(0) { }
- Location(quint32 l, quint32 c)
- : Location()
+ Location() : m_data(QSpecialIntegerBitfieldZero) {}
+ Location(quint32 l, quint32 c) : Location()
{
- line = l;
- column = c;
- Q_ASSERT(line == l);
- Q_ASSERT(column == c);
+ m_data.set<LineField>(l);
+ m_data.set<ColumnField>(c);
+ Q_ASSERT(m_data.get<LineField>() == l);
+ Q_ASSERT(m_data.get<ColumnField>() == c);
}
inline bool operator<(const Location &other) const {
- return line < other.line ||
- (line == other.line && column < other.column);
+ return m_data.get<LineField>() < other.m_data.get<LineField>()
+ || (m_data.get<LineField>() == other.m_data.get<LineField>()
+ && m_data.get<ColumnField>() < other.m_data.get<ColumnField>());
}
friend size_t qHash(const Location &location, size_t seed = 0)
{
- return QT_PREPEND_NAMESPACE(qHash)(location._dummy, seed);
+ return QT_PREPEND_NAMESPACE(qHash)(location.m_data.data(), seed);
+ }
+
+ friend bool operator==(const Location &a, const Location &b)
+ {
+ return a.m_data.data()== b.m_data.data();
+ }
+
+ void set(quint32 line, quint32 column)
+ {
+ m_data.set<LineField>(line);
+ m_data.set<ColumnField>(column);
}
- friend bool operator==(const Location &a, const Location &b) { return a._dummy == b._dummy; }
+ quint32 line() const { return m_data.get<LineField>(); }
+ quint32 column() const { return m_data.get<ColumnField>(); }
+
+private:
+ using LineField = quint32_le_bitfield_member<0, 20>;
+ using ColumnField = quint32_le_bitfield_member<20, 12>;
+
+ quint32_le_bitfield_union<LineField, ColumnField> m_data;
};
static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -168,13 +184,21 @@ struct RegExp
RegExp_Unicode = 0x08,
RegExp_Sticky = 0x10
};
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 5> flags;
- quint32_le_bitfield<5, 27> stringIndex;
- };
- RegExp() : _dummy(0) { }
+ RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
+ RegExp(quint32 flags, quint32 stringIndex) : RegExp()
+ {
+ m_data.set<FlagsField>(flags);
+ m_data.set<StringIndexField>(stringIndex);
+ }
+
+ quint32 flags() const { return m_data.get<FlagsField>(); }
+ quint32 stringIndex() const { return m_data.get<StringIndexField>(); }
+
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 5>;
+ using StringIndexField = quint32_le_bitfield_member<5, 27>;
+ quint32_le_bitfield_union<FlagsField, StringIndexField> m_data;
};
static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -187,25 +211,40 @@ struct Lookup
Type_QmlContextPropertyGetter = 3
};
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 4> type_and_flags;
- quint32_le_bitfield<4, 28> nameIndex;
- };
+ quint32 typeAndFlags() const { return m_data.get<TypeAndFlagsField>(); }
+ quint32 nameIndex() const { return m_data.get<NameIndexField>(); }
- Lookup() : _dummy(0) { }
+ Lookup() : m_data(QSpecialIntegerBitfieldZero) {}
+ Lookup(Type type, quint32 nameIndex) : Lookup()
+ {
+ m_data.set<TypeAndFlagsField>(type);
+ m_data.set<NameIndexField>(nameIndex);
+ }
+
+private:
+ using TypeAndFlagsField = quint32_le_bitfield_member<0, 4>;
+ using NameIndexField = quint32_le_bitfield_member<4, 28>;
+ quint32_le_bitfield_union<TypeAndFlagsField, NameIndexField> m_data;
};
static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 31> nameOffset;
- quint32_le_bitfield<31, 1> isAccessor;
- };
+ JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {}
+
+ void set(quint32 nameOffset, bool isAccessor)
+ {
+ m_data.set<NameOffsetField>(nameOffset);
+ m_data.set<IsAccessorField>(isAccessor ? 1 : 0);
+ }
+
+ quint32 nameOffset() const { return m_data.get<NameOffsetField>(); }
+ bool isAccessor() const { return m_data.get<IsAccessorField>() != 0; }
- JSClassMember() : _dummy(0) { }
+private:
+ using NameOffsetField = quint32_le_bitfield_member<0, 31>;
+ using IsAccessorField = quint32_le_bitfield_member<31, 1>;
+ quint32_le_bitfield_union<NameOffsetField, IsAccessorField> m_data;
};
static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -265,11 +304,26 @@ enum class BuiltinType : unsigned int {
struct ParameterType
{
- union {
- quint32 _dummy;
- quint32_le_bitfield<0, 1> indexIsBuiltinType;
- quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType;
- };
+ void set(bool indexIsBuiltinType, quint32 typeNameIndexOrBuiltinType)
+ {
+ m_data.set<IndexIsBuiltinTypeField>(indexIsBuiltinType ? 1 : 0);
+ m_data.set<TypeNameIndexOrBuiltinTypeField>(typeNameIndexOrBuiltinType);
+ }
+
+ bool indexIsBuiltinType() const
+ {
+ return m_data.get<IndexIsBuiltinTypeField>() != 0;
+ }
+
+ quint32 typeNameIndexOrBuiltinType() const
+ {
+ return m_data.get<TypeNameIndexOrBuiltinTypeField>();
+ }
+
+private:
+ using IndexIsBuiltinTypeField = quint32_le_bitfield_member<0, 1>;
+ using TypeNameIndexOrBuiltinTypeField = quint32_le_bitfield_member<1, 31>;
+ quint32_le_bitfield_union<IndexIsBuiltinTypeField, TypeNameIndexOrBuiltinTypeField> m_data;
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -287,7 +341,8 @@ struct Function
enum Flags : unsigned int {
IsStrict = 0x1,
IsArrowFunction = 0x2,
- IsGenerator = 0x4
+ IsGenerator = 0x4,
+ IsClosureWrapper = 0x8,
};
// Absolute offset into file where the code for this function is located.
@@ -448,7 +503,7 @@ struct Binding
{
quint32_le propertyNameIndex;
- enum ValueType : unsigned int {
+ enum Type : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
@@ -462,7 +517,7 @@ struct Binding
Type_GroupProperty
};
- enum Flags : unsigned int {
+ enum Flag : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
@@ -475,11 +530,20 @@ struct Binding
IsFunctionExpression = 0x200,
IsPropertyObserver = 0x400
};
+ Q_DECLARE_FLAGS(Flags, Flag);
+
+ using FlagsField = quint32_le_bitfield_member<0, 16>;
+ using TypeField = quint32_le_bitfield_member<16, 16>;
+ quint32_le_bitfield_union<FlagsField, TypeField> flagsAndType;
+
+ void clearFlags() { flagsAndType.set<FlagsField>(0); }
+ void setFlag(Flag flag) { flagsAndType.set<FlagsField>(flagsAndType.get<FlagsField>() | flag); }
+ bool hasFlag(Flag flag) const { return Flags(flagsAndType.get<FlagsField>()) & flag; }
+ Flags flags() const { return Flags(flagsAndType.get<FlagsField>()); }
+
+ void setType(Type type) { flagsAndType.set<TypeField>(type); }
+ Type type() const { return Type(flagsAndType.get<TypeField>()); }
- union {
- quint32_le_bitfield<0, 16> flags;
- quint32_le_bitfield<16, 16> type;
- };
union {
bool b;
quint32_le constantValueIndex;
@@ -493,24 +557,31 @@ struct Binding
Location location;
Location valueLocation;
+ bool hasSignalHandlerBindingFlag() const
+ {
+ const Flags bindingFlags = flags();
+ return bindingFlags & IsSignalHandlerExpression
+ || bindingFlags & IsSignalHandlerObject
+ || bindingFlags & IsPropertyObserver;
+ }
+
bool isValueBinding() const
{
- if (type == Type_AttachedProperty
- || type == Type_GroupProperty)
- return false;
- if (flags & IsSignalHandlerExpression
- || flags & IsSignalHandlerObject
- || flags & IsPropertyObserver)
+ switch (type()) {
+ case Type_AttachedProperty:
+ case Type_GroupProperty:
return false;
- return true;
+ default:
+ return !hasSignalHandlerBindingFlag();
+ }
}
- bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); }
- bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); }
+ bool isValueBindingNoAlias() const { return isValueBinding() && !hasFlag(IsBindingToAlias); }
+ bool isValueBindingToAlias() const { return isValueBinding() && hasFlag(IsBindingToAlias); }
bool isSignalHandler() const
{
- if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject || flags & IsPropertyObserver) {
+ if (hasSignalHandlerBindingFlag()) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isAttachedProperty());
Q_ASSERT(!isGroupProperty());
@@ -521,7 +592,7 @@ struct Binding
bool isAttachedProperty() const
{
- if (type == Type_AttachedProperty) {
+ if (type() == Type_AttachedProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isGroupProperty());
@@ -532,7 +603,7 @@ struct Binding
bool isGroupProperty() const
{
- if (type == Type_GroupProperty) {
+ if (type() == Type_GroupProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isAttachedProperty());
@@ -541,7 +612,7 @@ struct Binding
return false;
}
- bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
+ bool isFunctionExpression() const { return hasFlag(IsFunctionExpression); }
//reverse of Lexer::singleEscape()
static QString escapedString(const QString &string)
@@ -586,16 +657,19 @@ struct Binding
return tmp;
}
- bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
+ bool isTranslationBinding() const
+ {
+ const Binding::Type bindingType = type();
+ return bindingType == Type_Translation || bindingType == Type_TranslationById;
+ }
+ bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); }
bool valueAsBoolean() const
{
- if (type == Type_Boolean)
+ if (type() == Type_Boolean)
return value.b;
return false;
}
-
};
static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -668,37 +742,57 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected
struct Property
{
- quint32_le nameIndex;
- union {
- quint32_le_bitfield<0, 28> builtinTypeOrTypeNameIndex;
- quint32_le_bitfield<28, 1> isRequired;
- quint32_le_bitfield<29, 1> isBuiltinType;
- quint32_le_bitfield<30, 1> isList;
- quint32_le_bitfield<31, 1> isReadOnly;
- };
+private:
+ using BuiltinTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>;
+ using IsRequiredField = quint32_le_bitfield_member<28, 1>;
+ using IsBuiltinTypeField = quint32_le_bitfield_member<29, 1>;
+ using IsListField = quint32_le_bitfield_member<30, 1>;
+ using IsReadOnlyField = quint32_le_bitfield_member<31, 1>;
+public:
+ quint32_le nameIndex;
+ quint32_le_bitfield_union<
+ BuiltinTypeOrTypeNameIndexField,
+ IsRequiredField,
+ IsBuiltinTypeField,
+ IsListField,
+ IsReadOnlyField> data;
Location location;
void setBuiltinType(BuiltinType t)
{
- builtinTypeOrTypeNameIndex = static_cast<quint32>(t);
- isBuiltinType = true;
+ data.set<BuiltinTypeOrTypeNameIndexField>(static_cast<quint32>(t));
+ data.set<IsBuiltinTypeField>(true);
}
+
BuiltinType builtinType() const {
- if (isBuiltinType)
- return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex));
+ if (data.get<IsBuiltinTypeField>() != 0)
+ return BuiltinType(data.get<BuiltinTypeOrTypeNameIndexField>());
return BuiltinType::InvalidBuiltin;
}
+
void setCustomType(int nameIndex)
{
- builtinTypeOrTypeNameIndex = nameIndex;
- isBuiltinType = false;
+ data.set<BuiltinTypeOrTypeNameIndexField>(nameIndex);
+ data.set<IsBuiltinTypeField>(false);
}
int customType() const
{
- return isBuiltinType ? -1 : builtinTypeOrTypeNameIndex;
+ return data.get<IsBuiltinTypeField>() ? -1 : data.get<BuiltinTypeOrTypeNameIndexField>();
}
+
+ bool isBuiltinType() const { return data.get<IsBuiltinTypeField>(); }
+ uint builtinTypeOrTypeNameIndex() const { return data.get<BuiltinTypeOrTypeNameIndexField>(); }
+
+ bool isList() const { return data.get<IsListField>(); }
+ void setIsList(bool isList) { data.set<IsListField>(isList); }
+
+ bool isRequired() const { return data.get<IsRequiredField>(); }
+ void setIsRequired(bool isRequired) { data.set<IsRequiredField>(isRequired); }
+
+ bool isReadOnly() const { return data.get<IsReadOnlyField>(); }
+ void setIsReadOnly(bool isReadOnly) { data.set<IsReadOnlyField>(isReadOnly); }
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -709,20 +803,31 @@ struct RequiredPropertyExtraData {
static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias {
- enum Flags : unsigned int {
+private:
+ using NameIndexField = quint32_le_bitfield_member<0, 29>;
+ using FlagsField = quint32_le_bitfield_member<29, 3>;
+
+ // object id index (in QQmlContextData::idValues)
+ using TargetObjectIdField = quint32_le_bitfield_member<0, 31>;
+ using AliasToLocalAliasField = quint32_le_bitfield_member<31, 1>;
+
+public:
+
+ enum Flag : unsigned int {
IsReadOnly = 0x1,
Resolved = 0x2,
AliasPointsToPointerObject = 0x4
};
- union {
- quint32_le_bitfield<0, 29> nameIndex;
- quint32_le_bitfield<29, 3> flags;
- };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ quint32_le_bitfield_union<NameIndexField, FlagsField> nameIndexAndFlags;
+
union {
quint32_le idIndex; // string index
- quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
- quint32_le_bitfield<31, 1> aliasToLocalAlias;
+ quint32_le_bitfield_union<TargetObjectIdField, AliasToLocalAliasField>
+ targetObjectIdAndAliasToLocalAlias;
};
+
union {
quint32_le propertyNameIndex; // string index
qint32_le encodedMetaPropertyIndex;
@@ -731,16 +836,67 @@ struct Alias {
Location location;
Location referenceLocation;
- bool isObjectAlias() const {
- Q_ASSERT(flags & Resolved);
+ bool hasFlag(Flag flag) const
+ {
+ return nameIndexAndFlags.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ nameIndexAndFlags.set<FlagsField>(nameIndexAndFlags.get<FlagsField>() | flag);
+ }
+
+ void clearFlags()
+ {
+ nameIndexAndFlags.set<FlagsField>(0);
+ }
+
+ quint32 nameIndex() const
+ {
+ return nameIndexAndFlags.get<NameIndexField>();
+ }
+
+ void setNameIndex(quint32 nameIndex)
+ {
+ nameIndexAndFlags.set<NameIndexField>(nameIndex);
+ }
+
+ bool isObjectAlias() const
+ {
+ Q_ASSERT(hasFlag(Resolved));
return encodedMetaPropertyIndex == -1;
}
+
+ bool isAliasToLocalAlias() const
+ {
+ return targetObjectIdAndAliasToLocalAlias.get<AliasToLocalAliasField>();
+ }
+
+ void setIsAliasToLocalAlias(bool isAliasToLocalAlias)
+ {
+ targetObjectIdAndAliasToLocalAlias.set<AliasToLocalAliasField>(isAliasToLocalAlias);
+ }
+
+ quint32 targetObjectId() const
+ {
+ return targetObjectIdAndAliasToLocalAlias.get<TargetObjectIdField>();
+ }
+
+ void setTargetObjectId(quint32 targetObjectId)
+ {
+ targetObjectIdAndAliasToLocalAlias.set<TargetObjectIdField>(targetObjectId);
+ }
};
static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Object
{
- enum Flags : unsigned int {
+private:
+ using FlagsField = quint32_le_bitfield_member<0, 15>;
+ using DefaultPropertyIsAliasField = quint32_le_bitfield_member<15, 1>;
+ using IdField = quint32_le_bitfield_member<16, 16, qint32>;
+public:
+ enum Flag : unsigned int {
NoFlag = 0x0,
IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
HasDeferredBindings = 0x2, // any of the bindings are deferred
@@ -748,17 +904,15 @@ struct Object
IsInlineComponentRoot = 0x8,
InPartOfInlineComponent = 0x10
};
+ Q_DECLARE_FLAGS(Flags, Flag);
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
quint32_le inheritedTypeNameIndex;
quint32_le idNameIndex;
- union {
- quint32_le_bitfield<0, 15> flags;
- quint32_le_bitfield<15, 1> defaultPropertyIsAlias;
- qint32_le_bitfield<16, 16> id;
- };
+ quint32_le_bitfield_union<FlagsField, DefaultPropertyIsAliasField, IdField>
+ flagsAndDefaultPropertyIsAliasAndId;
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
quint16_le nFunctions;
quint16_le nProperties;
@@ -787,6 +941,48 @@ struct Object
// InlineComponent[]
// RequiredPropertyExtraData[]
+ Flags flags() const
+ {
+ return Flags(flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>());
+ }
+
+ bool hasFlag(Flag flag) const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() & flag;
+ }
+
+ void setFlag(Flag flag)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(
+ flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() | flag);
+ }
+
+ void setFlags(Flags flags)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(flags);
+ }
+
+ bool hasAliasAsDefaultProperty() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<DefaultPropertyIsAliasField>();
+ }
+
+ void setHasAliasAsDefaultProperty(bool defaultAlias)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<DefaultPropertyIsAliasField>(defaultAlias);
+ }
+
+ qint32 objectId() const
+ {
+ return flagsAndDefaultPropertyIsAliasAndId.get<IdField>();
+ }
+
+ void setObjectId(qint32 id)
+ {
+ flagsAndDefaultPropertyIsAliasAndId.set<IdField>(id);
+ }
+
+
static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
{
return ( sizeof(Object)
@@ -1138,8 +1334,8 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto prop = obj->propertiesBegin();
auto const propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
- if (!prop->isBuiltinType) {
- TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location);
+ if (!prop->isBuiltinType()) {
+ TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex(), prop->location);
r.errorWhenNotFound = true;
}
}
@@ -1147,7 +1343,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto binding = obj->bindingsBegin();
auto const bindingEnd = obj->bindingsEnd();
for ( ; binding != bindingEnd; ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty)
this->add(binding->propertyNameIndex, binding->location);
}
@@ -1329,7 +1525,7 @@ public:
QString bindingValueAsString(const CompiledData::Binding *binding) const
{
using namespace CompiledData;
- switch (binding->type) {
+ switch (binding->type()) {
case Binding::Type_Script:
case Binding::Type_String:
return stringAt(binding->stringIndex);
@@ -1352,14 +1548,14 @@ public:
QString bindingValueAsScriptString(const CompiledData::Binding *binding) const
{
- return (binding->type == CompiledData::Binding::Type_String)
+ return (binding->type() == CompiledData::Binding::Type_String)
? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
: bindingValueAsString(binding);
}
double bindingValueAsNumber(const CompiledData::Binding *binding) const
{
- if (binding->type != CompiledData::Binding::Type_Number)
+ if (binding->type() != CompiledData::Binding::Type_Number)
return 0.0;
return constants[binding->value.constantValueIndex].doubleValue();
}
@@ -1386,7 +1582,8 @@ public:
template<typename Char>
bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
{
- auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; });
+ const quint32_le oldFlags = mutableFlags();
+ auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; });
mutableFlags() |= temporaryFlags;
return writer(data<Char>(), size());
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 3b1d9f4595..9739c936da 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -76,7 +76,7 @@ void Object::simplifyRequiredProperties() {
for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) {
auto requiredIt = required.find(it->nameIndex);
if (requiredIt != required.end()) {
- it->isRequired = true;
+ it->setIsRequired(true);
required.erase(requiredIt);
}
}
@@ -106,20 +106,18 @@ bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::J
bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex)
{
- paramType->indexIsBuiltinType = false;
- paramType->typeNameIndexOrBuiltinType = 0;
const QString typeName = stringGenerator->stringForIndex(typeNameIndex);
auto builtinType = stringToBuiltinType(typeName);
if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) {
- if (typeName.isEmpty() || !typeName.at(0).isUpper())
+ if (typeName.isEmpty() || !typeName.at(0).isUpper()) {
+ paramType->set(false, 0);
return false;
- paramType->indexIsBuiltinType = false;
- paramType->typeNameIndexOrBuiltinType = typeNameIndex;
+ }
Q_ASSERT(quint32(typeNameIndex) < (1u << 31));
+ paramType->set(false, typeNameIndex);
} else {
- paramType->indexIsBuiltinType = true;
- paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType);
Q_ASSERT(quint32(builtinType) < (1u << 31));
+ paramType->set(true, static_cast<quint32>(builtinType));
}
return true;
}
@@ -172,7 +170,7 @@ QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typ
void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex,
const QV4::CompiledData::Location &loc)
{
- Q_ASSERT(loc.line > 0 && loc.column > 0);
+ Q_ASSERT(loc.line() > 0 && loc.column() > 0);
inheritedTypeNameIndex = typeNameIndex;
location = loc;
idNameIndex = idIndex;
@@ -197,8 +195,8 @@ QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &il
QSet<int> functionNames;
for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
Function *f = functionit.ptr;
- errorLocation->startLine = f->location.line;
- errorLocation->startColumn = f->location.column;
+ errorLocation->startLine = f->location.line();
+ errorLocation->startColumn = f->location.column();
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
@@ -258,6 +256,10 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool
if (p->nameIndex == prop->nameIndex)
return tr("Duplicate property name");
+ for (Alias *a = target->aliases->first; a; a = a->next)
+ if (a->nameIndex() == prop->nameIndex)
+ return tr("Property duplicates alias name");
+
if (propertyName.constData()->isUpper())
return tr("Property names cannot begin with an upper case letter");
@@ -278,12 +280,19 @@ QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefau
if (!target)
target = this;
- auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){
- return targetAlias.nameIndex == alias->nameIndex;
+ const auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){
+ return targetAlias.nameIndex() == alias->nameIndex();
});
if (aliasWithSameName != target->aliases->end())
return tr("Duplicate alias name");
+ const auto aliasSameAsProperty = std::find_if(target->properties->begin(), target->properties->end(), [&alias](const Property &targetProp){
+ return targetProp.nameIndex == alias->nameIndex();
+ });
+
+ if (aliasSameAsProperty != target->properties->end())
+ return tr("Alias has same name as existing property");
+
if (aliasName.constData()->isUpper())
return tr("Alias names cannot begin with an upper case letter");
@@ -322,13 +331,17 @@ void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraDat
QString Object::appendBinding(Binding *b, bool isListBinding)
{
const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
- if (!isListBinding && !bindingToDefaultProperty
- && b->type != QV4::CompiledData::Binding::Type_GroupProperty
- && b->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ if (!isListBinding
+ && !bindingToDefaultProperty
+ && b->type() != QV4::CompiledData::Binding::Type_GroupProperty
+ && b->type() != QV4::CompiledData::Binding::Type_AttachedProperty
+ && !b->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
Binding *existing = findBinding(b->propertyNameIndex);
- if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment))
+ if (existing
+ && existing->isValueBinding() == b->isValueBinding()
+ && !existing->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
return tr("Property value set multiple times");
+ }
}
if (bindingToDefaultProperty)
insertSorted(b);
@@ -396,8 +409,7 @@ void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString
import->type = QV4::CompiledData::Import::ImportScript;
import->uriIndex = jsGenerator->registerString(jsfile);
import->qualifierIndex = jsGenerator->registerString(module);
- import->location.line = lineNumber;
- import->location.column = column;
+ import->location.set(lineNumber, column);
document->imports << import;
}
@@ -408,8 +420,7 @@ void ScriptDirectivesCollector::importModule(const QString &uri, const QString &
import->uriIndex = jsGenerator->registerString(uri);
import->version = IRBuilder::extractVersion(QStringView(version));
import->qualifierIndex = jsGenerator->registerString(module);
- import->location.line = lineNumber;
- import->location.column = column;
+ import->location.set(lineNumber, column);
document->imports << import;
}
@@ -594,8 +605,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast)
inlineComponent->nameIndex = registerString(ast->name.toString());
inlineComponent->objectIndex = idx;
auto location = ast->firstSourceLocation();
- inlineComponent->location.line = location.startLine;
- inlineComponent->location.column = location.startColumn;
+ inlineComponent->location.set(location.startLine, location.startColumn);
_object->appendInlineComponent(inlineComponent);
return false;
}
@@ -791,8 +801,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
import->version = QTypeRevision();
}
- import->location.line = node->importToken.startLine;
- import->location.column = node->importToken.startColumn;
+ import->location.set(node->importToken.startLine, node->importToken.startColumn);
import->uriIndex = registerString(uri);
@@ -821,8 +830,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node)
return false;
}
- pragma->location.line = node->pragmaToken.startLine;
- pragma->location.column = node->pragmaToken.startColumn;
+ pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn);
_pragmas.append(pragma);
return false;
@@ -855,8 +863,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
if (enumName.at(0).isLower())
COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter"));
- enumeration->location.line = node->enumToken.startLine;
- enumeration->location.column = node->enumToken.startColumn;
+ enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn);
enumeration->enumValues = New<PoolList<EnumValue>>();
@@ -875,8 +882,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range"));
enumValue->value = e->value;
- enumValue->location.line = e->memberToken.startLine;
- enumValue->location.column = e->memberToken.startColumn;
+ enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn);
enumeration->enumValues->append(enumValue);
e = e->next;
@@ -900,8 +906,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
signal->nameIndex = registerString(signalName);
QQmlJS::SourceLocation loc = node->typeToken;
- signal->location.line = loc.startLine;
- signal->location.column = loc.startColumn;
+ signal->location.set(loc.startLine, loc.startColumn);
signal->parameters = New<PoolList<Parameter> >();
@@ -950,8 +955,8 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
const QStringView &name = node->name;
Property *property = New<Property>();
- property->isReadOnly = node->isReadonlyMember;
- property->isRequired = node->isRequired;
+ property->setIsReadOnly(node->isReadonlyMember);
+ property->setIsRequired(node->isRequired);
QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType);
bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin;
@@ -963,7 +968,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
property->setCustomType(registerString(memberType));
if (typeModifier == QLatin1String("list")) {
- property->isList = true;
+ property->setIsList(true);
} else if (!typeModifier.isEmpty()) {
recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
return false;
@@ -983,8 +988,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
property->nameIndex = registerString(propName);
QQmlJS::SourceLocation loc = node->firstSourceLocation();
- property->location.line = loc.startLine;
- property->location.column = loc.startColumn;
+ property->location.set(loc.startLine, loc.startColumn);
QQmlJS::SourceLocation errorLocation;
QString error;
@@ -1028,8 +1032,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
Function *f = New<Function>();
QQmlJS::SourceLocation loc = funDecl->identifierToken;
- f->location.line = loc.startLine;
- f->location.column = loc.startColumn;
+ f->location.set(loc.startLine, loc.startColumn);
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
@@ -1102,32 +1105,31 @@ QStringView IRBuilder::textRefAt(const QQmlJS::SourceLocation &first, const QQml
void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
{
QQmlJS::SourceLocation loc = statement->firstSourceLocation();
- binding->valueLocation.line = loc.startLine;
- binding->valueLocation.column = loc.startColumn;
- binding->type = QV4::CompiledData::Binding::Type_Invalid;
- if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
- binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
+ binding->valueLocation.set(loc.startLine, loc.startColumn);
+ binding->setType(QV4::CompiledData::Binding::Type_Invalid);
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly())
+ binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration);
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (exprStmt) {
QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = registerString(lit->value.toString());
} else if (QQmlJS::AST::TemplateLiteral *templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
templateLit && templateLit->hasNoSubstitution) {
// A template literal without substitution is just a string.
// With substitution, it could however be an arbitrarily complex expression
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = registerString(templateLit->value.toString());
} else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) {
- binding->type = QV4::CompiledData::Binding::Type_Boolean;
+ binding->setType(QV4::CompiledData::Binding::Type_Boolean);
binding->value.b = true;
} else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) {
- binding->type = QV4::CompiledData::Binding::Type_Boolean;
+ binding->setType(QV4::CompiledData::Binding::Type_Boolean);
binding->value.b = false;
} else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value));
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
@@ -1136,21 +1138,21 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
// below.
}
} else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
- binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression;
+ binding->setFlag(QV4::CompiledData::Binding::IsFunctionExpression);
} else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value));
}
} else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) {
- binding->type = QV4::CompiledData::Binding::Type_Null;
+ binding->setType(QV4::CompiledData::Binding::Type_Null);
binding->value.nullMarker = 0;
}
}
// Do binding instead
- if (binding->type == QV4::CompiledData::Binding::Type_Invalid) {
- binding->type = QV4::CompiledData::Binding::Type_Script;
+ if (binding->type() == QV4::CompiledData::Binding::Type_Invalid) {
+ binding->setType(QV4::CompiledData::Binding::Type_Script);
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
@@ -1161,7 +1163,15 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
binding->value.compiledScriptIndex = index;
// We don't need to store the binding script as string, except for script strings
// and types with custom parsers. Those will be added later in the compilation phase.
- binding->stringIndex = emptyStringIndex;
+ // Except that we cannot recover the string when cachegen runs; we need to therefore retain
+ // "undefined". Any other "special" strings (for the various literals) are already handled above
+ QQmlJS::AST::Node *nodeForString = statement;
+ if (exprStmt)
+ nodeForString = exprStmt->expression;
+ if (asStringRef(nodeForString) == u"undefined")
+ binding->stringIndex = registerString(u"undefined"_qs);
+ else
+ binding->stringIndex = emptyStringIndex;
}
}
@@ -1206,7 +1216,7 @@ void IRBuilder::tryGeneratingTranslationBinding(QStringView base, AST::ArgumentL
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_Translation;
+ binding->setType(QV4::CompiledData::Binding::Type_Translation);
binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("qsTrId")) {
QV4::CompiledData::TranslationData translationData;
@@ -1239,7 +1249,7 @@ void IRBuilder::tryGeneratingTranslationBinding(QStringView base, AST::ArgumentL
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_TranslationById;
+ binding->setType(QV4::CompiledData::Binding::Type_TranslationById);
binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) {
if (!args || !args->expression)
@@ -1256,7 +1266,7 @@ void IRBuilder::tryGeneratingTranslationBinding(QStringView base, AST::ArgumentL
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = jsGenerator->registerString(str.toString());
} else if (base == QLatin1String("QT_TRANSLATE_NOOP")) {
if (!args || !args->expression)
@@ -1277,7 +1287,7 @@ void IRBuilder::tryGeneratingTranslationBinding(QStringView base, AST::ArgumentL
if (args)
return; // too many arguments, stop
- binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->setType(QV4::CompiledData::Binding::Type_String);
binding->stringIndex = jsGenerator->registerString(str.toString());
}
}
@@ -1314,9 +1324,8 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = nameLocation.offset;
- binding->location.line = nameLocation.startLine;
- binding->location.column = nameLocation.startColumn;
- binding->flags = 0;
+ binding->location.set(nameLocation.startLine, nameLocation.startColumn);
+ binding->clearFlags();
setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
@@ -1334,27 +1343,26 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = nameLocation.offset;
- binding->location.line = nameLocation.startLine;
- binding->location.column = nameLocation.startColumn;
+ binding->location.set(nameLocation.startLine, nameLocation.startColumn);
const Object *obj = _objects.at(objectIndex);
binding->valueLocation = obj->location;
- binding->flags = 0;
+ binding->clearFlags();
- if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
- binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly())
+ binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration);
// No type name on the initializer means it must be a group property
if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex)
- binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
+ binding->setType(Binding::Type_GroupProperty);
else
- binding->type = QV4::CompiledData::Binding::Type_Object;
+ binding->setType(Binding::Type_Object);
if (isOnAssignment)
- binding->flags |= QV4::CompiledData::Binding::IsOnAssignment;
+ binding->setFlag(Binding::IsOnAssignment);
if (isListItem)
- binding->flags |= QV4::CompiledData::Binding::IsListItem;
+ binding->setFlag(Binding::IsListItem);
binding->value.objectIndex = objectIndex;
QString error = bindingsTarget()->appendBinding(binding, isListItem);
@@ -1366,16 +1374,15 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio
bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
{
Alias *alias = New<Alias>();
- alias->flags = 0;
+ alias->clearFlags();
if (node->isReadonlyMember)
- alias->flags |= QV4::CompiledData::Alias::IsReadOnly;
+ alias->setFlag(QV4::CompiledData::Alias::IsReadOnly);
const QString propName = node->name.toString();
- alias->nameIndex = registerString(propName);
+ alias->setNameIndex(registerString(propName));
QQmlJS::SourceLocation loc = node->firstSourceLocation();
- alias->location.line = loc.startLine;
- alias->location.column = loc.startColumn;
+ alias->location.set(loc.startLine, loc.startColumn);
alias->propertyNameIndex = emptyStringIndex;
@@ -1389,8 +1396,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
rhsLoc = node->statement->firstSourceLocation();
else
rhsLoc = node->semicolonToken;
- alias->referenceLocation.line = rhsLoc.startLine;
- alias->referenceLocation.column = rhsLoc.startColumn;
+ alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn);
QStringList aliasReference;
@@ -1485,8 +1491,7 @@ bool IRBuilder::setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Sta
COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
_object->idNameIndex = registerString(idQString);
- _object->locationOfIdProperty.line = idLocation.startLine;
- _object->locationOfIdProperty.column = idLocation.startColumn;
+ _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn);
return true;
}
@@ -1532,19 +1537,19 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
binding->offset = qualifiedIdElement->identifierToken.offset;
- binding->location.line = qualifiedIdElement->identifierToken.startLine;
- binding->location.column = qualifiedIdElement->identifierToken.startColumn;
- binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine;
- binding->valueLocation.column = qualifiedIdElement->next->identifierToken.startColumn;
- binding->flags = 0;
+ binding->location.set(qualifiedIdElement->identifierToken.startLine,
+ qualifiedIdElement->identifierToken.startColumn);
+ binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine,
+ qualifiedIdElement->next->identifierToken.startColumn);
+ binding->clearFlags();
if (onAssignment)
- binding->flags |= QV4::CompiledData::Binding::IsOnAssignment;
+ binding->setFlag(QV4::CompiledData::Binding::IsOnAssignment);
if (isAttachedProperty)
- binding->type = QV4::CompiledData::Binding::Type_AttachedProperty;
+ binding->setType(QV4::CompiledData::Binding::Type_AttachedProperty);
else
- binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
+ binding->setType(QV4::CompiledData::Binding::Type_GroupProperty);
int objIndex = 0;
if (!defineQMLObject(&objIndex, nullptr, binding->location, nullptr, nullptr))
@@ -1605,7 +1610,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
{
- if (property->isBuiltinType || property->isList)
+ if (property->isBuiltinType() || property->isList())
return false;
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (!exprStmt)
@@ -1707,10 +1712,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
- objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias;
- objectToWrite->flags = o->flags;
+ objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias);
+ objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags));
objectToWrite->idNameIndex = o->idNameIndex;
- objectToWrite->id = o->id;
+ objectToWrite->setObjectId(o->id);
objectToWrite->location = o->location;
objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
@@ -1885,7 +1890,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
continue;
QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
*bindingToWrite = *b;
- if (b->type == QV4::CompiledData::Binding::Type_Script)
+ if (b->type() == QV4::CompiledData::Binding::Type_Script)
bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
bindingPtr += sizeof(QV4::CompiledData::Binding);
}
@@ -1924,6 +1929,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
}
+ /* We do not want to visit the whole function, as we already called enterQmlFunction
+ However, there might be a function defined as a default argument of the function.
+ That needs to be considered, too, so we call handleTopLevelFunctionFormals to
+ deal with them.
+ */
+ scan.handleTopLevelFunctionFormals(function);
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
@@ -1973,60 +1984,23 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots)
-{
- for (int i = 0; i < componentRoots.count(); ++i) {
- if (!compileComponent(componentRoots.at(i)))
- return false;
- }
-
- return compileComponent(/*root object*/0);
-}
-
-bool JSCodeGen::compileComponent(int contextObject)
-{
- const QmlIR::Object *obj = document->objects.at(contextObject);
- if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
- Q_ASSERT(obj->bindingCount() == 1);
- const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
- contextObject = componentBinding->value.objectIndex;
- }
-
- return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject);
-}
-
-bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
+bool JSCodeGen::generateRuntimeFunctions(QmlIR::Object *object)
{
- QmlIR::Object *object = document->objects.at(objectIndex);
- if (object->flags & QV4::CompiledData::Object::IsComponent && !object->isInlineComponent)
+ if (object->functionsAndExpressions->count == 0)
return true;
- for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it)
- compileComponent(it->objectIndex);
-
- if (object->functionsAndExpressions->count > 0) {
- QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
- for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
- functionsToCompile << *foe;
- const QVector<int> runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile);
- if (hasError())
- return false;
-
- object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(),
- runtimeFunctionIndices);
+ QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
+ functionsToCompile.reserve(object->functionsAndExpressions->count);
+ for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe;
+ foe = foe->next) {
+ functionsToCompile << *foe;
}
- for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
- if (binding->type < QV4::CompiledData::Binding::Type_Object)
- continue;
-
- int target = binding->value.objectIndex;
- int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
-
- if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope))
- return false;
- }
+ const auto runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile);
+ if (hasError())
+ return false;
+ object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(),
+ runtimeFunctionIndices);
return true;
}
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 99c1af6cbf..93e550a530 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -394,6 +394,10 @@ public:
int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); }
const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
+ bool hasFlag(QV4::CompiledData::Object::Flag flag) const { return flags & flag; }
+ qint32 objectId() const { return id; }
+ bool hasAliasAsDefaultProperty() const { return defaultPropertyIsAlias; }
+
private:
friend struct ::QQmlIRLoader;
@@ -591,9 +595,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
- bool generateCodeForComponents(const QVector<quint32> &componentRoots);
- bool compileComponent(int contextObject);
- bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
+ bool generateRuntimeFunctions(QmlIR::Object *object);
private:
Document *document;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index f7c4e2c98f..4f00131a09 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -3048,12 +3048,17 @@ bool Codegen::visit(ThisExpression *)
if (hasError())
return false;
- if (_context->isArrowFunction) {
- Reference r = referenceForName(QStringLiteral("this"), false);
- r.isReadonly = true;
- setExprResult(r);
- return false;
+ for (Context *parentContext = _context; parentContext; parentContext = parentContext->parent) {
+ if (parentContext->isArrowFunction) {
+ Reference r = referenceForName(QStringLiteral("this"), false);
+ r.isReadonly = true;
+ setExprResult(r);
+ return false;
+ }
+ if (parentContext->contextType != ContextType::Block)
+ break;
}
+
setExprResult(Reference::fromThis(this));
return false;
}
@@ -3158,6 +3163,17 @@ bool Codegen::visit(YieldExpression *ast)
return false;
}
+ auto innerMostCurentFunctionContext = _context;
+ while (innerMostCurentFunctionContext && innerMostCurentFunctionContext->contextType != ContextType::Function)
+ innerMostCurentFunctionContext = innerMostCurentFunctionContext->parent;
+
+ Q_ASSERT(innerMostCurentFunctionContext); // yield outside function would have been rejected by parser
+
+ if (!innerMostCurentFunctionContext->isGenerator) {
+ throwSyntaxError(ast->firstSourceLocation(), u"Yield is only valid in generator functions"_qs);
+ return false;
+ }
+
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 5511ed304a..fdb5fcb331 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -154,10 +154,7 @@ int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Getter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Getter, nameIndex);
return lookups.size() - 1;
}
@@ -168,49 +165,37 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Setter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Setter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_GlobalGetter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter;
- l.nameIndex = nameIndex;
- lookups << l;
+ lookups << CompiledData::Lookup(CompiledData::Lookup::Type_QmlContextPropertyGetter, nameIndex);
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
- CompiledData::RegExp re;
- re.stringIndex = registerString(regexp->pattern.toString());
-
- re.flags = 0;
+ quint32 flags = 0;
if (regexp->flags & QQmlJS::Lexer::RegExp_Global)
- re.flags |= CompiledData::RegExp::RegExp_Global;
+ flags |= CompiledData::RegExp::RegExp_Global;
if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
- re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
+ flags |= CompiledData::RegExp::RegExp_IgnoreCase;
if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
- re.flags |= CompiledData::RegExp::RegExp_Multiline;
+ flags |= CompiledData::RegExp::RegExp_Multiline;
if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode)
- re.flags |= CompiledData::RegExp::RegExp_Unicode;
+ flags |= CompiledData::RegExp::RegExp_Unicode;
if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky)
- re.flags |= CompiledData::RegExp::RegExp_Sticky;
+ flags |= CompiledData::RegExp::RegExp_Sticky;
- regexps.append(re);
+ regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString())));
return regexps.size() - 1;
}
@@ -243,8 +228,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
for (const auto &name : members) {
- member->nameOffset = registerString(name);
- member->isAccessor = false;
+ member->set(registerString(name), false);
++member;
}
@@ -432,9 +416,21 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->flags |= CompiledData::Function::IsArrowFunction;
if (irFunction->isGenerator)
function->flags |= CompiledData::Function::IsGenerator;
- function->nestedFunctionIndex =
- irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first()))
- : std::numeric_limits<uint32_t>::max();
+ if (irFunction->returnsClosure)
+ function->flags |= CompiledData::Function::IsClosureWrapper;
+
+ if (!irFunction->returnsClosure
+ || irFunction->innerFunctionAccessesThis
+ || irFunction->innerFunctionAccessesNewTarget) {
+ // If the inner function does things with this and new.target we need to do some work in
+ // the outer function. Then we shouldn't directly access the nested function.
+ function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max();
+ } else {
+ // Otherwise we can directly use the nested function.
+ function->nestedFunctionIndex
+ = quint32(module->functions.indexOf(irFunction->nestedContexts.first()));
+ }
+
function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
@@ -462,8 +458,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
currentOffset += function->nLabelInfos * sizeof(quint32);
}
- function->location.line = irFunction->line;
- function->location.column = irFunction->column;
+ function->location.set(irFunction->line, irFunction->column);
function->codeOffset = currentOffset;
function->codeSize = irFunction->code.size();
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 5155c25c06..457c150b5e 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -125,7 +125,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator {
int registerSetterLookup(int nameIndex);
int registerGlobalGetterLookup(int nameIndex);
int registerQmlContextPropertyGetterLookup(int nameIndex);
- int lookupNameIndex(int index) const { return lookups[index].nameIndex; }
+ int lookupNameIndex(int index) const { return lookups[index].nameIndex(); }
QString lookupName(int index) const { return stringForIndex(lookupNameIndex(index)); }
int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 9dec968a91..7a558478dd 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -57,10 +57,7 @@ using namespace QQmlJS::AST;
static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation)
{
- CompiledData::Location target;
- target.line = astLocation.startLine;
- target.column = astLocation.startColumn;
- return target;
+ return CompiledData::Location(astLocation.startLine, astLocation.startColumn);
}
@@ -382,8 +379,11 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
if (!_allowFuncDecls)
_cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
- if (!enterFunction(expr, /*enterName*/ true))
+ if (!enterFunction(expr, expr->identifierToken.length > 0
+ ? FunctionNameContext::Inner
+ : FunctionNameContext::None)) {
return false;
+ }
Node::accept(expr->formals, this);
Node::accept(expr->body, this);
leaveEnvironment();
@@ -399,7 +399,9 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
bool ScanFunctions::visit(FunctionExpression *ast)
{
- return enterFunction(ast, /*enterName*/ false);
+ return enterFunction(ast, ast->identifierToken.length > 0
+ ? FunctionNameContext::Inner
+ : FunctionNameContext::None);
}
bool ScanFunctions::visit(ClassExpression *ast)
@@ -496,12 +498,12 @@ bool ScanFunctions::visit(ArrayPattern *ast)
return false;
}
-bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+bool ScanFunctions::enterFunction(FunctionExpression *ast, FunctionNameContext nameContext)
{
Q_ASSERT(_context);
if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
- return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName);
+ return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, nameContext);
}
void ScanFunctions::endVisit(FunctionExpression *)
@@ -536,7 +538,7 @@ void ScanFunctions::endVisit(PatternProperty *)
bool ScanFunctions::visit(FunctionDeclaration *ast)
{
- return enterFunction(ast, /*enterName*/ true);
+ return enterFunction(ast, FunctionNameContext::Outer);
}
void ScanFunctions::endVisit(FunctionDeclaration *)
@@ -674,7 +676,9 @@ void ScanFunctions::endVisit(WithStatement *)
leaveEnvironment();
}
-bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
+bool ScanFunctions::enterFunction(
+ Node *ast, const QString &name, FormalParameterList *formals, StatementList *body,
+ FunctionNameContext nameContext)
{
Context *outerContext = _context;
enterEnvironment(ast, ContextType::Function, name);
@@ -685,7 +689,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
if (outerContext) {
outerContext->hasNestedFunctions = true;
// The identifier of a function expression cannot be referenced from the enclosing environment.
- if (enterName) {
+ if (nameContext == FunctionNameContext::Outer) {
if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) {
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
return false;
@@ -711,8 +715,10 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
}
- if (!enterName && (!name.isEmpty() && (!formals || !formals->containsName(name))))
+ if (nameContext == FunctionNameContext::Inner
+ && (!name.isEmpty() && (!formals || !formals->containsName(name)))) {
_context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
+ }
_context->formals = formals;
if (body && !_context->isStrict)
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 0336398cac..d2868fc428 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -83,15 +83,32 @@ public:
ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
void operator()(QQmlJS::AST::Node *node);
+ // see comment at its call site in generateJSCodeForFunctionsAndBindings
+ // for why this function is necessary
+ void handleTopLevelFunctionFormals(QQmlJS::AST::FunctionExpression *node) {
+ if (node && node->formals)
+ node->formals->accept(this);
+ }
+
void enterGlobalEnvironment(ContextType compilationMode);
void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode,
const QString &name);
void leaveEnvironment();
void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast)
- { enterFunction(ast, false); }
+ { enterFunction(ast, FunctionNameContext::None); }
protected:
+ // Function declarations add their name to the outer scope, but not the
+ // inner scope. Function expressions add their name to the inner scope,
+ // unless the name is actually picked from the outer scope rather than
+ // given after the function token. QML functions don't add their name
+ // anywhere because the name is already recorded in the QML element.
+ // This enum is used to control the behavior of enterFunction().
+ enum class FunctionNameContext {
+ None, Inner, Outer
+ };
+
using Visitor::visit;
using Visitor::endVisit;
@@ -118,7 +135,8 @@ protected:
bool visit(QQmlJS::AST::FieldMemberExpression *) override;
bool visit(QQmlJS::AST::ArrayPattern *) override;
- bool enterFunction(QQmlJS::AST::FunctionExpression *ast, bool enterName);
+ bool enterFunction(QQmlJS::AST::FunctionExpression *ast,
+ FunctionNameContext nameContext);
void endVisit(QQmlJS::AST::FunctionExpression *) override;
@@ -161,7 +179,7 @@ protected:
protected:
bool enterFunction(QQmlJS::AST::Node *ast, const QString &name,
QQmlJS::AST::FormalParameterList *formals,
- QQmlJS::AST::StatementList *body, bool enterName);
+ QQmlJS::AST::StatementList *body, FunctionNameContext nameContext);
void calcEscapingVariables();
// fields:
diff --git a/src/qml/configure.cmake b/src/qml/configure.cmake
index 11c29620d6..a587a8fe1d 100644
--- a/src/qml/configure.cmake
+++ b/src/qml/configure.cmake
@@ -7,9 +7,12 @@
#### Libraries
-# special case begin
qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST MODULE_NAME qml QMAKE_LIB lttng-ust)
-# special case end
+qt_find_package(Python REQUIRED)
+if(Python_Interpreter_FOUND)
+ # Need to make it globally available to the project
+ set(QT_INTERNAL_DECLARATIVE_PYTHON "${Python_EXECUTABLE}" CACHE STRING "")
+endif()
#### Tests
@@ -200,13 +203,9 @@ qt_feature("qml-xmllistmodel" PRIVATE
CONDITION QT_FEATURE_qml_itemmodel AND QT_FEATURE_future
)
-# special case begin
-qt_qml_find_python(__qt_qml_python_path __qt_qml_python_found)
-# special case end
-
qt_feature("qml-python" PRIVATE
LABEL "python"
- CONDITION __qt_qml_python_found # special case
+ CONDITION Python_Interpreter_FOUND
)
qt_configure_add_summary_section(NAME "Qt QML")
qt_configure_add_summary_entry(ARGS "qml-network")
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 58b8ea2c4f..0ca56f3a70 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -44,17 +44,26 @@
#include <private/qqmlengine_p.h>
#include <private/qv4compileddata_p.h>
+#include <atomic>
#include <cstdio>
QT_REQUIRE_CONFIG(qml_debug);
QT_BEGIN_NAMESPACE
+#if __cplusplus >= 202002L
+# define Q_ATOMIC_FLAG_INIT {}
+#else
+# define Q_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT // deprecated in C++20
+#endif
+
+static std::atomic_flag s_printedWarning = Q_ATOMIC_FLAG_INIT;
+
QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning)
{
- if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning)
+ if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed))
fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n");
- QQmlEnginePrivate::qml_debugging_enabled = true;
+ QQmlEnginePrivate::qml_debugging_enabled.store(true, std::memory_order_relaxed);
}
/*!
diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp
index 74b10e64d7..a576cddf1a 100644
--- a/src/qml/debugger/qqmldebugconnector.cpp
+++ b/src/qml/debugger/qqmldebugconnector.cpp
@@ -111,7 +111,7 @@ QQmlDebugConnector *QQmlDebugConnector::instance()
if (!params)
return nullptr;
- if (!QQmlEnginePrivate::qml_debugging_enabled) {
+ if (!QQmlEnginePrivate::qml_debugging_enabled.load(std::memory_order_relaxed)) {
if (!params->arguments.isEmpty()) {
qWarning().noquote() << QString::fromLatin1(
"QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging "
diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h
index d1ad90adfd..4f7b013160 100644
--- a/src/qml/debugger/qqmldebugconnector_p.h
+++ b/src/qml/debugger/qqmldebugconnector_p.h
@@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlDebugConnector
{
+ virtual ~QQmlDebugConnector() = default; // don't break 'override' on ~QQmlDebugServer
public:
static QQmlDebugConnector *instance() { return nullptr; }
diff --git a/src/qml/types/qqmlmodelindexvaluetype.cpp b/src/qml/debugger/qqmldebugserver.cpp
index cbf2fef348..5119fc4209 100644
--- a/src/qml/types/qqmlmodelindexvaluetype.cpp
+++ b/src/qml/debugger/qqmldebugserver.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,32 +37,13 @@
**
****************************************************************************/
-#include "qqmlmodelindexvaluetype_p.h"
+#include "qqmldebugserver_p.h"
QT_BEGIN_NAMESPACE
-/*!
- \internal
-*/
-QString QQmlModelIndexValueType::propertiesString(const QModelIndex &idx)
-{
- if (!idx.isValid())
- return QLatin1String("()");
- return QString(QLatin1String("(%1,%2,0x%3,%4(0x%5))"))
- .arg(idx.row()).arg(idx.column()).arg(idx.internalId(), 0, 16)
- .arg(QLatin1String(idx.model()->metaObject()->className())).arg(quintptr(idx.model()), 0, 16);
-}
-
-/*!
- \internal
-*/
-QString QQmlItemSelectionRangeValueType::toString() const
-{
- return QString(QLatin1String("QItemSelectionRange(%1,%2)"))
- .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.topLeft())->toString())
- .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.bottomRight())->toString());
-}
+QQmlDebugServer::~QQmlDebugServer()
+ = default;
QT_END_NAMESPACE
-#include "moc_qqmlmodelindexvaluetype_p.cpp"
+#include "moc_qqmldebugserver_p.cpp"
diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h
index e848b00bda..c99155051c 100644
--- a/src/qml/debugger/qqmldebugserver_p.h
+++ b/src/qml/debugger/qqmldebugserver_p.h
@@ -62,6 +62,7 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugServer : public QQmlDebugConnector
{
Q_OBJECT
public:
+ ~QQmlDebugServer() override;
virtual void setDevice(QIODevice *socket) = 0;
};
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
index 5d58e4d2e0..d01e472edb 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h
+++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
@@ -108,7 +108,6 @@ class QQmlEngineControlService {};
class QQmlNativeDebugService {};
class QQmlDebugTranslationService {
public:
- virtual QString foundElidedText(QObject *, const QString &, const QString &) {return {};}
virtual void foundTranslationBinding(const TranslationBindingInformation &) {}
};
@@ -186,7 +185,6 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugTranslationService : public QQmlDebugService
public:
static const QString s_key;
- virtual QString foundElidedText(QObject *qQuickTextObject, const QString &layoutText, const QString &elideText) = 0;
virtual void foundTranslationBinding(const TranslationBindingInformation &translationBindingInformation) = 0;
protected:
friend class QQmlDebugConnector;
diff --git a/src/qml/debugger/qqmldebugtranslationprotocol_p.h b/src/qml/debugger/qqmldebugtranslationprotocol_p.h
index 467b34d509..06d3850510 100644
--- a/src/qml/debugger/qqmldebugtranslationprotocol_p.h
+++ b/src/qml/debugger/qqmldebugtranslationprotocol_p.h
@@ -64,19 +64,23 @@ enum class Request {
ChangeLanguage = 1,
StateList,
ChangeState,
- MissingTranslations,
+ TranslationIssues,
TranslatableTextOccurrences,
WatchTextElides,
DisableWatchTextElides,
+ // following are obsolete, just provided for compilation compatibility
+ MissingTranslations
};
enum class Reply {
LanguageChanged = 101,
StateList,
StateChanged,
- MissingTranslations,
+ TranslationIssues,
TranslatableTextOccurrences,
- TextElided,
+ // following are obsolete, just provided for compilation compatibility
+ MissingTranslations,
+ TextElided
};
inline QByteArray createChangeLanguageRequest(QDataStream &packet, const QUrl &url,
@@ -98,6 +102,12 @@ inline QByteArray createMissingTranslationsRequest(QDataStream &packet)
return qobject_cast<QBuffer *>(packet.device())->data();
}
+inline QByteArray createTranslationIssuesRequest(QDataStream &packet)
+{
+ packet << Request::TranslationIssues;
+ return qobject_cast<QBuffer *>(packet.device())->data();
+}
+
inline QByteArray createTranslatableTextOccurrencesRequest(QDataStream &packet)
{
packet << Request::TranslatableTextOccurrences;
@@ -210,7 +220,7 @@ public:
>> qmlElement.propertyName >> qmlElement.translationId >> qmlElement.translatedText
>> qmlElement.fontFamily >> qmlElement.fontPointSize >> qmlElement.fontPixelSize
>> qmlElement.fontStyleName >> qmlElement.horizontalAlignment
- >> qmlElement.verticalAlignment;
+ >> qmlElement.verticalAlignment >> qmlElement.stateName;
}
friend QDataStream &operator<<(QDataStream &stream, const QmlElement &qmlElement)
@@ -220,7 +230,7 @@ public:
<< qmlElement.translatedText << qmlElement.fontFamily
<< qmlElement.fontPointSize << qmlElement.fontPixelSize
<< qmlElement.fontStyleName << qmlElement.horizontalAlignment
- << qmlElement.verticalAlignment;
+ << qmlElement.verticalAlignment << qmlElement.stateName;
}
CodeMarker codeMarker;
@@ -232,10 +242,12 @@ public:
QString elementId;
QString elementType;
qreal fontPointSize = 0.0;
+ QString stateName;
int fontPixelSize = 0;
int horizontalAlignment = 0;
int verticalAlignment = 0;
};
+
class QmlState
{
public:
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index f7dc326def..9e2ba98ca4 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -176,7 +176,7 @@ public:
RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url,
const QV4::CompiledData::Object *obj, const QString &type)
- : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
+ : Location(QQmlSourceLocation(type, obj->location.line(), obj->location.column()), url),
locationType(Creating), sent(false)
{
unit = ref;
diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf
index b2f2c0cb77..820e7c67d2 100644
--- a/src/qml/doc/qtqml.qdocconf
+++ b/src/qml/doc/qtqml.qdocconf
@@ -65,5 +65,3 @@ manifestmeta.thumbnail.names += "QtQml/Chapter 4*" \
navigation.landingpage = "Qt QML"
navigation.cppclassespage = "Qt QML C++ Classes"
navigation.qmltypespage = "Qt QML QML Types"
-
-macro.versionlessNote = "If \\l{Versionless commands}{versionless commands} are disabled, use \\c{\1} instead. It supports the same set of arguments as this command."
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt b/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt
new file mode 100644
index 0000000000..d33f009e38
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.19)
+project(qt_target_qml_sources_snippet)
+
+set(CMAKE_AUTOMOC TRUE)
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+# ![0]
+set_source_files_properties(nested/way/down/File.qml PROPERTIES
+ QT_RESOURCE_ALIAS File.qml
+)
+set_source_files_properties(TemplateFile.qml PROPERTIES
+ QT_RESOURCE_ALIAS templates/File.qml
+ QT_QML_SKIP_QMLDIR_ENTRY TRUE
+ QT_QML_SKIP_QMLLINT TRUE
+ QT_QML_SKIP_CACHEGEN TRUE
+)
+set_source_files_properties(FunnySingleton.qml PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+)
+qt_add_qml_module(qt_target_qml_sources_example
+ URI Example
+ VERSION 2.3
+ RESOURCE_PREFIX /my.company.com/imports
+ QML_FILES
+ nested/way/down/File.qml
+ TemplateFile.qml
+ FunnySingleton.qml
+)
+
+set_source_files_properties(some_old_thing.qml PROPERTIES
+ QT_QML_SOURCE_VERSIONS "1.1;2.0"
+ QT_QML_SOURCE_TYPENAME OldThing
+)
+set_source_files_properties(../../../images/button-types.png PROPERTIES
+ QT_RESOURCE_ALIAS button-types.png
+)
+qt_target_qml_sources(qt_target_qml_sources_example
+ QML_FILES some_old_thing.qml
+ RESOURCES
+ ../../../images/button-types.png
+ doc/README.txt
+)
+# ![0]
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/FunnySingleton.qml b/src/qml/doc/snippets/cmake/qt_target_qml_sources/FunnySingleton.qml
new file mode 100644
index 0000000000..4aec01c32d
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/FunnySingleton.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+pragma Singleton
+import QtQml
+
+QtObject {}
+//! [0]
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/TemplateFile.qml b/src/qml/doc/snippets/cmake/qt_target_qml_sources/TemplateFile.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/TemplateFile.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/doc/README.txt b/src/qml/doc/snippets/cmake/qt_target_qml_sources/doc/README.txt
new file mode 100644
index 0000000000..52d51e034d
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/doc/README.txt
@@ -0,0 +1 @@
+Dummy file, contents not important.
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/nested/way/down/File.qml b/src/qml/doc/snippets/cmake/qt_target_qml_sources/nested/way/down/File.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/nested/way/down/File.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/some_old_thing.qml b/src/qml/doc/snippets/cmake/qt_target_qml_sources/some_old_thing.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/some_old_thing.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/src/qml/doc/snippets/code/doc_src_qtqml.cmake b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
index c2e5d3d9d2..c312201ff8 100644
--- a/src/qml/doc/snippets/code/doc_src_qtqml.cmake
+++ b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
@@ -1,4 +1,4 @@
#! [0]
-find_package(Qt6 COMPONENTS Qml REQUIRED)
+find_package(Qt6 REQUIRED COMPONENTS Qml)
target_link_libraries(mytarget PRIVATE Qt6::Qml)
#! [0]
diff --git a/src/qml/doc/snippets/qml/CMakeLists.txt b/src/qml/doc/snippets/qml/CMakeLists.txt
new file mode 100644
index 0000000000..24e775341f
--- /dev/null
+++ b/src/qml/doc/snippets/qml/CMakeLists.txt
@@ -0,0 +1,11 @@
+qt_add_library(extra_module STATIC)
+qt_add_qml_module(extra_module
+ URI "ExtraModule"
+ VERSION 1.0
+ QML_FILES
+ Extra.qml
+ SOURCES
+ extrathing.cpp extrathing.h
+)
+
+add_subdirectory(ExtraModule)
diff --git a/src/qml/doc/snippets/qml/MajorProject-CMakeLists.txt b/src/qml/doc/snippets/qml/MajorProject-CMakeLists.txt
new file mode 100644
index 0000000000..3e5cbd4565
--- /dev/null
+++ b/src/qml/doc/snippets/qml/MajorProject-CMakeLists.txt
@@ -0,0 +1,23 @@
+
+set_source_files_properties(Thing.qml
+ PROPERTIES
+ QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0"
+)
+
+set_source_files_properties(OtherThing.qml
+ PROPERTIES
+ QT_QML_SOURCE_VERSIONS "2.2;3.0"
+)
+
+qt_add_qml_module(my_module
+ URI MyModule
+ VERSION 3.2
+ PAST_MAJOR_VERSIONS
+ 1 2
+ QML_FILES
+ Thing.qml
+ OtherThing.qml
+ OneMoreThing.qml
+ SOURCES
+ everything.cpp everything.h
+)
diff --git a/src/qml/doc/snippets/qml/createQmlObject.qml b/src/qml/doc/snippets/qml/createQmlObject.qml
index 8a082a71de..bfb1d98ca8 100644
--- a/src/qml/doc/snippets/qml/createQmlObject.qml
+++ b/src/qml/doc/snippets/qml/createQmlObject.qml
@@ -58,9 +58,18 @@ Rectangle {
function createIt() {
//![0]
-var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',
- parentItem,
- "dynamicSnippet1");
+const newObject = Qt.createQmlObject(`
+ import QtQuick 2.0
+
+ Rectangle {
+ color: "red"
+ width: 20
+ height: 20
+ }
+ `,
+ parentItem,
+ "myDynamicSnippet"
+);
//![0]
//![destroy]
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
index 86c3e078c8..1326b8c87a 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
+++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs
@@ -50,8 +50,9 @@
//![0]
// script.mjs
import { factorial } from "factorial.mjs"
+export { factorial }
-function showCalculations(value) {
+export function showCalculations(value) {
console.log(
"Call factorial() from script.js:",
factorial(value));
diff --git a/src/qml/doc/snippets/qml/myProject-CMakeLists.txt b/src/qml/doc/snippets/qml/myProject-CMakeLists.txt
new file mode 100644
index 0000000000..49c63a1513
--- /dev/null
+++ b/src/qml/doc/snippets/qml/myProject-CMakeLists.txt
@@ -0,0 +1,13 @@
+qt_add_executable(main_program main.cpp)
+
+qt_add_qml_module(main_program
+ VERSION 1.0
+ URI myProject
+ QML_FILES
+ main.qml
+ SOURCES
+ onething.cpp onething.h
+
+)
+
+target_link_libraries(main_program PRIVATE extra_moduleplugin)
diff --git a/src/qml/doc/snippets/qml/myimageprovider.txt b/src/qml/doc/snippets/qml/myimageprovider.txt
new file mode 100644
index 0000000000..4605734398
--- /dev/null
+++ b/src/qml/doc/snippets/qml/myimageprovider.txt
@@ -0,0 +1,15 @@
+qt_add_qml_module(imageproviderplugin
+ VERSION 1.0
+ URI "ImageProvider"
+ PLUGIN_TARGET imageproviderplugin
+ NO_PLUGIN_OPTIONAL
+ NO_GENERATE_PLUGIN_SOURCE
+ CLASS_NAME ImageProviderExtensionPlugin
+ QML_FILES
+ AAA.qml
+ BBB.qml
+ SOURCES
+ moretypes.cpp moretypes.h
+ myimageprovider.cpp myimageprovider.h
+ plugin.cpp
+)
diff --git a/src/qml/doc/snippets/qml/plugin.cpp.txt b/src/qml/doc/snippets/qml/plugin.cpp.txt
new file mode 100644
index 0000000000..02d1112a4a
--- /dev/null
+++ b/src/qml/doc/snippets/qml/plugin.cpp.txt
@@ -0,0 +1,14 @@
+#include <myimageprovider.h>
+#include <QtQml/qqmlextensionplugin.h>
+
+class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
+public:
+ void initializeEngine(QQmlEngine *engine, const char *uri) final
+ {
+ Q_UNUSED(uri);
+ engine->addImageProvider("myimg", new MyImageProvider);
+ }
+};
diff --git a/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp
new file mode 100644
index 0000000000..0064d66a5c
--- /dev/null
+++ b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+//! [qjs-as-container]
+
+ class Cache : public QObject
+ {
+ Q_OBJECT
+ QML_ELEMENT
+
+ public:
+ Q_INVOKABLE QJSValue lookup(const QString &key) {
+ if (auto it = m_cache.constFind(key); it != m_cache.constEnd()) {
+ return *it; // impicit conversion
+ } else {
+ return QJSValue::UndefinedValue; // implicit conversion
+ }
+ }
+
+ QHash<QString, QString> m_cache;
+ }
+
+//! [qjs-as-container]
diff --git a/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsengine.cpp b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsengine.cpp
new file mode 100644
index 0000000000..59ee30c25f
--- /dev/null
+++ b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/exampleqjsengine.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![qjs-engine-example]
+
+ QJSEngine engine;
+ // We create an object with a read-only property whose getter throws an exception
+ auto val = engine.evaluate("let o = { get f() {throw 42;} }; o");
+ val.property("f");
+ qDebug() << engine.hasError(); // prints false
+
+ // This time, we construct a QJSManagedValue before accessing the property
+ val = engine.evaluate("let o = { get f() {throw 42;} }; o");
+ QJSManagedValue managed(std::move(val), &engine);
+ managed.property("f");
+ qDebug() << engine.hasError(); // prints true
+
+ QJSValue error = engine.catchError();
+ Q_ASSERT(error.toInt(), 42);
+
+//![qjs-engine-example]
diff --git a/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/qjsengine.cpp b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/qjsengine.cpp
new file mode 100644
index 0000000000..e8622b2c50
--- /dev/null
+++ b/src/qml/doc/snippets/qtjavascript/integratingjswithcpp/qjsengine.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![qjs-engine]
+
+ QJSEngine engine;
+ QJSValue object = engine.newObject();
+ object.setProperty("num", 42);
+ QJSValue function = engine.evaluate("(o) => o.num *= 2 ");
+ QJSValueList args = { object };
+ QJSValue result = function.call(args);
+ QJSValue expected = "84";
+ Q_ASSERT(result.equals(expected) && !result.strictlyEquals(expected));
+
+//![qjs-engine]
+
diff --git a/src/qml/doc/src/cmake/cmake-properties.qdoc b/src/qml/doc/src/cmake/cmake-properties.qdoc
new file mode 100644
index 0000000000..eccc815eca
--- /dev/null
+++ b/src/qml/doc/src/cmake/cmake-properties.qdoc
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\group cmake-source-file-properties-qtqml
+\title CMake Source File Properties in Qt6 Qml
+
+\l{CMake Commands in Qt6 Qml}{CMake Commands} know about the following CMake
+source file properties:
+
+\sa{CMake Property Reference}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_INTERNAL_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_INTERNAL_TYPE
+\target cmake-source-file-property-QT_QML_INTERNAL_TYPE
+
+\summary {Marks a QML file as providing an internal type.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to indicate that the \c{.qml} file provides an internal type.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SINGLETON_TYPE.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SINGLETON_TYPE
+\target cmake-source-file-property-QT_QML_SINGLETON_TYPE
+
+\summary {Marks a QML file as providing a singleton type.}
+
+\cmakepropertysince 6.2
+
+A \c{.qml} file that provides a singleton type needs to have its \c QT_QML_SINGLETON_TYPE source
+property set to \c TRUE to ensure that the singleton command is written into the
+\l{Module Definition qmldir Files}{qmldir} file.
+This must be done in addition to the QML file containing the \c {pragma Singleton} statement.
+
+See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
+how to set the \c QT_QML_SINGLETON_TYPE property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_CACHEGEN.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_CACHEGEN
+\target cmake-source-file-property-QT_QML_SKIP_CACHEGEN
+
+\summary {Excludes a file from being compiled to byte code.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the \c{.qml} file from being compiled to byte code.
+The file will still be added to the \c target as a resource in uncompiled form
+(see \l{qmlcachegen-auto}{Caching compiled QML sources}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLDIR_ENTRY
+\target cmake-source-file-property-QT_QML_SKIP_QMLDIR_ENTRY
+
+\summary {Excludes a file from being added as a type to the QML module's typeinfo file.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent
+the \c{.qml} file from being added as a type to the QML module's typeinfo file
+(see \l{qmldir-autogeneration}{Auto-generating \c{qmldir} and typeinfo files}).
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SKIP_QMLLINT.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SKIP_QMLLINT
+\target cmake-source-file-property-QT_QML_SKIP_QMLLINT
+
+\summary {Prevents a file from being included in automatic qmllint processing.}
+
+\cmakepropertysince 6.2
+
+Set this property to \c TRUE to prevent the file from being included in
+\l{qmllint-auto}{automatic qmllint processing}.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_TYPENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_TYPENAME
+\target cmake-source-file-property-QT_QML_SOURCE_TYPENAME
+
+\summary {Overrides the type name provided by the file.}
+
+\cmakepropertysince 6.2
+
+Use this property to override the \c QML type name provided by this file.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QML_SOURCE_VERSIONS.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QML_SOURCE_VERSIONS
+\target cmake-source-file-property-QT_QML_SOURCE_VERSIONS
+
+\summary {Specifies a custom set of versions for a type.}
+
+\cmakepropertysince 6.2
+
+When the file needs to provide type entries for a custom set of versions,
+for example when the QML types were first introduced in a minor patch
+version after the \c{.0} release, specify those versions using this property.
+
+\sa{qml-source-file-properties}{qt_target_qml_sources}
+*/
+
+
+/*!
+\page cmake-source-file-property-QT_QMLTC_FILE_BASENAME.html
+\ingroup cmake-source-file-properties-qtqml
+
+\title QT_QMLTC_FILE_BASENAME
+\target cmake-source-file-property-QT_QMLTC_FILE_BASENAME
+
+\summary {Specifies a non-default .h and .cpp file name.}
+
+\cmakepropertysince 6.3
+\preliminarycmakeproperty
+
+Use this property to specify a non-default \c .h and \c .cpp file name, which helps to resolve
+conflicting file names.
+
+\sa{qt_target_compile_qml_to_cpp}
+*/
diff --git a/src/qml/doc/src/cmake/cmake-variables.qdoc b/src/qml/doc/src/cmake/cmake-variables.qdoc
index 901ce26e91..8f5b87b1cb 100644
--- a/src/qml/doc/src/cmake/cmake-variables.qdoc
+++ b/src/qml/doc/src/cmake/cmake-variables.qdoc
@@ -37,18 +37,34 @@
The \l{qt6_add_qml_module}{qt6_add_qml_module()} command accepts an
\c OUTPUT_DIRECTORY argument which specifies where the QML module's \c qmldir
-file, typeinfo file and plugin library will be created. By default, the current
-binary directory (\c CMAKE_CURRENT_BINARY_DIR) is used if that argument is not
-provided. When a set of QML modules are being defined, it may be convenient to
-have them all generated under a common point in the build directory. If the
-source directory structure doesn't match the URI structure of the QML modules,
-or if you just want your QML modules to be collected under a different
-location, the \c QT_QML_OUTPUT_DIRECTORY can be used. When set, the default
-changes to the concatenation of \c QT_QML_OUTPUT_DIRECTORY and the QML module's
-\e{target path}, which is based on the module URI. \c QT_QML_OUTPUT_DIRECTORY
-will also be added to the import path of the \c qmllint and \c qmlcachegen
-tooling targets, allowing them to find other QML modules under the same base
-location.
+file, typeinfo file and plugin library will be created. When that argument is
+not used, the default value is based on the \c QT_QML_OUTPUT_DIRECTORY variable,
+if it is set. If \c QT_QML_OUTPUT_DIRECTORY is not set, the default value
+depends on the type of backing target (see the
+\l{qt6_add_qml_module#OUTPUT_DIRECTORY}{OUTPUT_DIRECTORY} documentation for
+details).
+
+When \c QT_QML_OUTPUT_DIRECTORY is set, the default output directory will be
+formed by appending the QML module's \e{target path} (which is based on the
+module URI) to \c QT_QML_OUTPUT_DIRECTORY.
+The \c QT_QML_OUTPUT_DIRECTORY will also be added to the import path of the
+\c qmllint and \c qmlcachegen tooling targets, allowing them to find other QML
+modules under the same base location. This allows the project to use a source
+directory structure that doesn't exactly match the URI structure of the QML
+modules, or to merge sets of QML modules under a common base point.
+
+When building QML modules for Android, \c QT_QML_OUTPUT_DIRECTORY is set to
+\c{${CMAKE_BINARY_DIR}/android-qml} by default. The Android deployment routine
+uses this directory to locate the required QML modules in the build tree.
+The output directory of a QML module can also be set for a project or for each
+QML module target by the user and it can therefore differ from the Android
+default directory. In this case, to successfully deploy the executable on
+Android, the \e{target path} of the QML module must be based on the module URI.
+Also, the \l{cmake-target-property-QT_QML_IMPORT_PATH}{QT_QML_IMPORT_PATH}
+property of the executable target must contain the import paths of all QML
+modules that are built in the project tree, have a custom output directory,
+and are used by the executable target. This behavior will likely change in
+a future Qt version due to improvements in the build system implementation.
*/
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index 4c3655651b..b81a0c80ea 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -34,6 +34,8 @@
\brief Defines a QML module.
+\cmakecommandsince 6.2
+
\section1 Synopsis
\badcode
@@ -54,8 +56,10 @@ qt_add_qml_module(
[IMPORT_PATH ...]
[SOURCES ...]
[QML_FILES ...]
+ [RESOURCES ...]
[OUTPUT_TARGETS out_targets_var]
[DESIGNER_SUPPORTED]
+ [NO_PLUGIN]
[NO_PLUGIN_OPTIONAL]
[NO_CREATE_PLUGIN_TARGET]
[NO_GENERATE_PLUGIN_SOURCE]
@@ -63,11 +67,15 @@ qt_add_qml_module(
[NO_GENERATE_QMLDIR]
[NO_LINT]
[NO_CACHEGEN]
+ [NO_RESOURCE_TARGET_PATH]
)
\endcode
-\versionlessNote qt6_add_qml_module()
+\versionlessCMakeCommandsNote qt6_add_qml_module()
+
+See \l {Building a QML application} and \l {Building a reusable QML module}
+for examples that define QML modules.
\section1 Description
@@ -90,6 +98,22 @@ first command argument. C++ sources, \c{.qml} files, and resources should all
be added to the backing target. The backing target is a library that should be
installed in the same location as any other library defined by the project.
+The source directory structure under which the backing target is created should
+match the target path of the QML module (the target path is the module's URI
+with dots replaced by forward slashes). If the source directory structure
+doesn't match the target path, \c{qt_add_qml_module()} will issue a warning.
+
+The following example shows a suitable source directory structure for a QML
+module with a URI of \c{MyThings.Panels}. The call to \c{qt_add_qml_module()}
+would be in the \c{CMakeLists.txt} file shown.
+
+\badcode
+src
+ +-- MyThings
+ +-- Panels
+ +-- CMakeLists.txt
+\endcode
+
A separate \e plugin target is associated with the QML module. It is used at
runtime to load the module dynamically when the application doesn't already
link to the backing target. The plugin target will also be a library and is
@@ -106,6 +130,10 @@ For cases where the QML module needs a custom plugin class implementation, the
\l{NO_GENERATE_PLUGIN_SOURCE} and usually the \l{NO_PLUGIN_OPTIONAL} options
will be needed.
+\note
+When using static linking, it migt be necessary to use
+\c Q_IMPORT_QML_PLUGIN to ensure that the QML plugin is correctly linked.
+
\section3 Plugin target with no backing target
A QML module can be defined with the plugin target serving as its own backing
@@ -128,11 +156,16 @@ load-time performance.
\section3 Executable as a QML module
An executable target can act as a backing target for a QML module. In this case,
-there should be no need for a plugin library, since the QML module will always
-be loaded directly as part of the application. The \c{qt_add_qml_module()}
-command will detect when an executable is used as the backing target and will
-automatically disable the creation of a separate plugin. Do not use any of the
-options with \c{PLUGIN} in their name when using this arrangement.
+there will be no plugin library, since the QML module will always be loaded
+directly as part of the application. The \c{qt_add_qml_module()} command will
+detect when an executable is used as the backing target and will automatically
+disable the creation of a separate plugin. Do not use any of the options with
+\c{PLUGIN} in their name when using this arrangement.
+
+When an executable is used as the backing target, the source directory structure
+is not expected to match the QML module's target path.
+See \l{qmlcachegen-auto}{Caching compiled QML sources} for additional target
+path differences for compiled-in resources.
\target qmldir-autogeneration
@@ -146,19 +179,27 @@ The \l OUTPUT_DIRECTORY argument determines where the \c qmldir and typeinfo
files will be written to. If the QML module has a plugin, that plugin will also
be created in the same directory as the \c qmldir file.
-In static builds, the backing target's \c{.qml} files will be scanned during
-the CMake configure run to determine the imports used by the module and set up
-linking relationships. When a \c{.qml} file is added to or removed from the
-module, CMake will normally re-run automatically and the relevant files will be
-re-scanned, since a \c{CMakeLists.txt} file will have been modified. During the
-course of development, an existing \c{.qml} file may add or remove an import or
-a type. On its own, this would not cause CMake to re-run automatically, so you
-should explicitly re-run CMake to force the \c qmldir file to be regenerated
-and any linking relationships to be updated.
+If using a statically built Qt, the backing target's \c{.qml} files will be
+scanned during the CMake configure run to determine the imports used by the
+module and set up linking relationships. When a \c{.qml} file is added to or
+removed from the module, CMake will normally re-run automatically and the
+relevant files will be re-scanned, since a \c{CMakeLists.txt} file will have
+been modified. During the course of development, an existing \c{.qml} file may
+add or remove an import or a type. On its own, this would not cause CMake to
+re-run automatically, so you should explicitly re-run CMake to force the
+\c qmldir file to be regenerated and any linking relationships to be updated.
The backing target's C++ sources are scanned at build time to generate a
typeinfo file and a C++ file to register the associated types. The generated
C++ file is automatically added to the backing target as a source.
+This requires \c AUTOMOC to be enabled on the target. The project is
+responsible for ensuring this, usually by setting the \c CMAKE_AUTOMOC variable
+to \c TRUE before calling \c qt_add_qml_module(), or by passing in an existing
+target with the \c AUTOMOC target property already set to \c TRUE. It isn't an
+error to have \c AUTOMOC disabled on the target, but the project is then
+responsible for handling the consequences. This may include having to manually
+generate the typeinfo file instead of allowing it to be auto-generated with
+missing details, and adding C++ code to register the types.
Projects should prefer to use the auto-generated typeinfo and \c qmldir files
where possible. They are easier to maintain and they don't suffer from the same
@@ -183,7 +224,7 @@ may still be needed in certain situations by the QML engine.
The resource path of each file is determined by its path relative to the
current source directory (\c CMAKE_CURRENT_SOURCE_DIR). This resource path is
appended to a prefix formed by concatenating the \l{RESOURCE_PREFIX} and
-the target path (which is the URI with dots replaced with forward slashes).
+the target path (but see \l NO_RESOURCE_TARGET_PATH for an exception to this).
Ordinarily, the project should aim to place \c{.qml} files in
the same relative location as they would have in the resources. If the \c{.qml}
file is in a different relative directory to its desired resource path, its
@@ -220,17 +261,39 @@ found at the following resource paths:
A separate linting target will be automatically created if any \c{.qml} files
are added to the module via the \c QML_FILES keyword, or by a later call to
\l{qt6_target_qml_sources}{qt_target_qml_sources()}. The name of the linting
-target will be the \c target followed by \c{_qmllint}. The linting target is
-not part of the default CMake \c ALL target, it is intended for developers to
-execute manually on demand.
+target will be the \c target followed by \c{_qmllint}. An \c{all_qmllint}
+target which depends on all the individual \c{*_qmllint} targets is also
+provided as a convenience.
+
+\target qml-naming-js-files
+\section2 Naming conventions for \c{.js} files
+
+JavaScript file names that are intended to be addressed as components should
+start with an uppercase letter.
+
+Alternatively, you may use lowercase file names and set the source file
+property
+\l{cmake-source-file-property-QT_QML_SOURCE_TYPENAME}{QT_QML_SOURCE_TYPE_NAME}
+to the desired type name.
+
+\target qml-cmake-singletons
+\section2 Singletons
+
+If a QML module has \c{.qml} files which provide singleton types, these files
+need to have their \c QT_QML_SINGLETON_TYPE source property set to \c TRUE, to
+ensure that the \singleton command is written into the
+\l{Module Definition qmldir Files}{qmldir} file. This must be done in addition
+to the QML file containing the \c {pragma Singleton} statement.
+
+See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
+how to set the \c QT_QML_SINGLETON_TYPE property.
\section1 Arguments
The \c target specifies the name of the backing target for the QML module.
-By default, it will be created as a shared library if CMake's
-\c BUILD_SHARED_LIBS variable is set to true, or as a static library otherwise.
-This choice can be explicitly overridden with the \c STATIC or \c SHARED
-options.
+By default, it is created as a shared library if Qt was built as shared
+libraries, or as a static library otherwise. This choice can be explicitly
+overridden with the \c STATIC or \c SHARED options.
The plugin target associated with the QML module can be specified using the
\c PLUGIN_TARGET argument. The \c PLUGIN_TARGET can be the same as the backing
@@ -246,18 +309,28 @@ change the plugin's output name by setting target properties like
The backing \c target and the plugin target (if different) will be created by
the command, unless they already exist. Projects should generally let them be
created by the command so that they are created as the appropriate target type.
-If an existing \c target is passed in and it is an executable target, then no
-plugin target will be created or used.
+If the backing \c target is a static library, the plugin will also be created
+as a static library. If the backing \c target is a shared library, the plugin
+will be created as a module library. If an existing \c target is passed in and
+it is an executable target, there will be no plugin. If you intend to always
+link directly to the backing target and do not need a plugin, it can be
+disabled by adding the \c NO_PLUGIN option. Specifying both \c NO_PLUGIN and
+\c PLUGIN_TARGET is an error.
In certain situations, the project may want to delay creating the plugin target
until after the call. The \c NO_CREATE_PLUGIN_TARGET option can be given in
that situation. The project is then expected to call
\l{qt6_add_qml_plugin}{qt_add_qml_plugin()} on the plugin target once it has
-been created.
+been created. When \c NO_CREATE_PLUGIN_TARGET is given, \c PLUGIN_TARGET must
+also be provided to explicitly name the plugin target.
Every QML module must define a \c URI. It should be specified in dotted URI
-notation, such as \c{QtQuick.Layouts}. It must not contain anything other than
-alphanumeric or dot characters. Other QML modules may use this name in
+notation, such as \c{QtQuick.Layouts}. Each segment must be a well-formed
+ECMAScript Identifier Name. This means, for example, the segments
+must not start with a number and they must not contain \e{-} (minus)
+characters. As the \c URI will be translated into directory names, you
+should restrict it to alphanumeric characters of the latin alphabet,
+underscores, and dots. Other QML modules may use this name in
\l{qtqml-syntax-imports.html}{import statements} to import the module. The
\c URI will be used in the \c module line of the generated
\l{Module Definition qmldir Files}{qmldir} file. The \c URI is also used to
@@ -267,10 +340,26 @@ A QML module must also define a \c VERSION in the form \c{Major.Minor}, where
both \c Major and \c Minor must be integers. An additional \c{.Patch}
component may be appended, but will be ignored. A list of earlier major
versions the module provides types for can also optionally be given after the
-\c PAST_MAJOR_VERSIONS keyword.
+\c PAST_MAJOR_VERSIONS keyword (see below).
See \l{qtqml-modules-identifiedmodules.html}{Identified Modules} for further
in-depth discussion of the module URI and version numbering.
+A list of additional major versions the module provides may be given using the
+\c PAST_MAJOR_VERSIONS keyword. For each of those versions and each QML file
+without a \c QT_QML_SOURCE_VERSIONS setting an additional entry in the
+\l{Module Definition qmldir Files}{qmldir} file will be generated to specify
+the extra version. Furthermore, the generated module registration code will
+register the past major versions using \l{qmlRegisterModule()} on the C++ side.
+The module registration code is automatically generated for your QML module,
+unless you specify \c{NO_GENERATE_QMLTYPES} (but use of this option is strongly
+discouraged). Usage of \c PAST_MAJOR_VERSIONS adds some overhead when your
+module is imported. You should increment the major version of your module as
+rarely as possible. Once you can rely on all QML files importing this module to
+omit the version in their imports, you can safely omit \c{PAST_MAJOR_VERSIONS}.
+All the QML files will then import the latest version of your module. If you
+have to support versioned imports, consider supporting only a limited number of
+past major versions.
+
\target RESOURCE_PREFIX
\c RESOURCE_PREFIX is intended to encapsulate a namespace for the project and
will often be the same for all QML modules that the project defines. It should
@@ -287,20 +376,37 @@ qt_add_qml_module(someTarget
)
\endcode
+\target NO_RESOURCE_TARGET_PATH
+When various files are added to the compiled-in resources, they are placed
+under a path formed by concatenating the \c RESOURCE_PREFIX and the target path.
+For the special case where the backing target is an executable, it may be
+desirable to place the module's \c{.qml} files and other resources directly
+under the \c RESOURCE_PREFIX instead. This can be achieved by specifying the
+\c NO_RESOURCE_TARGET_PATH option, which may only be used if the backing target
+is an executable.
+
\target OUTPUT_DIRECTORY
\c OUTPUT_DIRECTORY specifies where the plugin library, \c qmldir and typeinfo
files are generated. When this keyword is not given, the default value will be
the target path (formed from the \c URI) appended to the value of the
\l QT_QML_OUTPUT_DIRECTORY variable.
-If that variable is not defined, then the output directory will be
-set to \c{${CMAKE_CURRENT_BINARY_DIR}}. When the structure of the source tree
+If that variable is not defined, the default depends on the type of backing
+target. For executables, the value will be the target path appended to
+\c{${CMAKE_CURRENT_BINARY_DIR}}, whereas for other targets it will be just
+\c{${CMAKE_CURRENT_BINARY_DIR}}. When the structure of the source tree
matches the structure of QML module target paths (which is highly recommended),
-\l QT_QML_OUTPUT_DIRECTORY often isn't needed. The need for specifying the
-\c OUTPUT_DIRECTORY keyword should be rare, but if it is used, it is likely
-that the caller will also need to add to the \l IMPORT_PATH to ensure that
-\l{qmllint-auto}{linting}, \l{qmlcachegen-auto}{cached compilation} of qml
-sources and \l{qt6_import_qml_plugins}{automatic importing} of plugins in
-static builds all work correctly.
+\l QT_QML_OUTPUT_DIRECTORY often isn't needed. In order to match the structure
+of the target paths, you have to call your directories \e exactly like the
+segments of your module URI. For example, if your module URI is
+\c{MyUpperCaseThing.mylowercasething}, you need to put this in a directory
+called \c{MyUpperCaseThing/mylowercasething/}.
+
+The need for specifying the \c OUTPUT_DIRECTORY keyword should be rare, but if
+it is used, it is likely that the caller will also need to add to the
+\l IMPORT_PATH to ensure that \l{qmllint-auto}{linting},
+\l{qmlcachegen-auto}{cached compilation} of qml sources and
+\l{qt6_import_qml_plugins}{automatic importing} of plugins in static builds all
+work correctly.
\target NO_GENERATE_PLUGIN_SOURCE
By default, \c{qt_add_qml_module()} will auto-generate a \c{.cpp} file that
@@ -311,7 +417,21 @@ plugin class, the \c NO_GENERATE_PLUGIN_SOURCE option should be given. Where no
\c CLASS_NAME is provided, it defaults to the \c URI with dots replaced by
underscores, then \c Plugin appended. Unless the QML module has no plugin, the
class name will be recorded as a \c classname line in the generated
-\l{Module Definition qmldir Files}{qmldir} file.
+\l{Module Definition qmldir Files}{qmldir} file. You need to add any C++ files
+with custom plugin code to the plugin target. Since the plugin then likely
+contains functionality that goes beyond simply loading the backing library, you
+will probably want to add \l{NO_PLUGIN_OPTIONAL}, too. Otherwise the QML engine
+may skip loading the plugin if it detects that the backing library is already
+linked.
+
+\target NO_PLUGIN
+If the \c NO_PLUGIN keyword is given, then no plugin will be built. This
+keyword is thus incompatible with all the options that customize the plugin
+target, in particular \l{NO_GENERATE_PLUGIN_SOURCE}, \l{NO_PLUGIN_OPTIONAL},
+\l{PLUGIN_TARGET}, \l{NO_CREATE_PLUGIN_TARGET}, and \l{CLASS_NAME}. If you do
+not provide a plugin for your module, it will only be fully usable if its
+backing library has been linked into the executable. It is generally hard to
+guarantee that a linker preserves the linkage to a library it considers unused.
\target NO_PLUGIN_OPTIONAL
If the \c NO_PLUGIN_OPTIONAL keyword is given, then the plugin is recorded in
@@ -336,13 +456,20 @@ typeinfo file will be generated, but the project will still be expected to
generate a typeinfo file and place it in the same directory as the generated
\c qmldir file.
-\c IMPORTS provides a list of other QML modules that this module imports.
-A version can be specified by appending it after a slash, such as
-\c{QtQuick/2.0}. The minor version may be omitted, as in \c{QtQuick/2}.
-Alternatively, \c auto may be given for the version (\c{QtQuick/auto}), which
-would result in the version that the current module is being imported with
-being used. Each module listed here will be added as an \c{import} entry in the
-generated \l{Module Definition qmldir Files}{qmldir} file.
+\c IMPORTS provides a list of other QML modules that this module imports. Each
+module listed here will be added as an \c{import} entry in the generated
+\l{Module Definition qmldir Files}{qmldir} file. If a QML file imports the
+this module, it also imports all the modules listed under \c{IMPORTS}.
+Optionally, a version can be specified by appending it after a slash, such as
+\c{QtQuick/2.0}. Omitting the version will cause the greatest version available
+to be imported. You may only specify the major version, as in \c{QtQuick/2}. In
+that case the greatest minor version available with the given major version will
+be imported. Finally, \c{auto} may be given as version (\c{QtQuick/auto}). If
+\c{auto} is given, the version that the current module is being imported with is
+propagated to the module to be imported. Given an entry \c{QtQuick/auto} in a
+module \c{YourModule}, if a QML file specifies \c{import YourModule 3.14}, this
+results in importing version \c{3.14} of \c{QtQuick}. For related modules that
+follow a common versioning scheme, you should use \c{auto}.
\c OPTIONAL_IMPORTS provides a list of other QML modules that this module
\e may import at run-time. These are not automatically imported by the QML
@@ -384,7 +511,8 @@ be added to the backing target after this command has been called.
\c RESOURCES lists any other files needed by the module, such as images
referenced from the QML code. These files will be added as compiled-in
-resources under the \l RESOURCE_PREFIX. If needed, their relative location can
+resources (see \l RESOURCE_PREFIX for an explanation of the base point they
+will be located under). If needed, their relative location can
be controlled by setting the \c QT_RESOURCE_ALIAS source property, just as for
\c{.qml} files (see \l{qmlcachegen-auto}{Caching compiled QML sources}).
@@ -400,8 +528,8 @@ and are referenced by the backing target's linking requirements as part of
ensuring that resources are set up and loaded correctly.
The \c DESIGNER_SUPPORTED keyword should be given if the QML module supports
-\l{Qt Quick Designer}. When present, the generated \c qmldir file will contain
+Qt Quick Designer. When present, the generated \c qmldir file will contain
a \c designersupported line. See \l{Module Definition qmldir Files} for how
-this affects the way Quick Designer handles the plugin.
+this affects the way Qt Quick Designer handles the plugin.
*/
diff --git a/src/qml/doc/src/cmake/qt_add_qml_plugin.qdoc b/src/qml/doc/src/cmake/qt_add_qml_plugin.qdoc
index 01dfc3eb16..fe63d678c7 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_plugin.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_plugin.qdoc
@@ -37,17 +37,104 @@
\section1 Synopsis
\badcode
-qt_add_qml_plugin(...)
+qt_add_qml_plugin(
+ target
+ [BACKING_TARGET backing_target]
+ [STATIC | SHARED]
+ [OUTPUT_DIRECTORY]
+ [URI]
+ [CLASS_NAME]
+ [NO_GENERATE_PLUGIN_SOURCE]
+)
\endcode
-\versionlessNote qt6_add_qml_plugin()
+\versionlessCMakeCommandsNote qt6_add_qml_plugin()
\section1 Description
-TBD
+This command creates the plugin target associated with a QML module. It would
+normally be called internally by \l{qt6_add_qml_module}{qt_add_qml_module()} to
+create or update the plugin associated with its backing target. You should not
+call this function directly unless you have special circumstances that require
+you to create the target in a special way.
+
+The documentation for \l{qt6_add_qml_module}{qt_add_qml_module()} describes
+different structural patterns for how the CMake targets associated with a QML
+module can be arranged. Note that even if the QML module has no separate backing
+target and all functionality is implemented directly in the plugin (not the
+recommended arrangement), you should still call
+\l{qt6_add_qml_module}{qt_add_qml_module()} rather than \c{qt_add_qml_plugin()}.
+
\section1 Arguments
-TBD
+The \c target specifies the name of the target to use for the QML plugin. If it
+does not already exist, it will be created.
+
+\c BACKING_TARGET specifies the name of the backing target that the plugin is
+associated with. The backing target can be the same as the plugin \c target, in
+which case there is only the one merged target, but this is not typically
+recommended (see \l{qt6_add_qml_module}{qt_add_qml_module()} for more
+information). \c BACKING_TARGET should always be provided unless there are
+special circumstances that require the plugin target to be created before the
+backing target. If \c BACKING_TARGET is not provided, a \c URI option must be
+given.
+
+By default, the plugin is created with a type that is compatible with the
+backing target. If the backing target is a static library, the plugin will also
+be created as a static library. If the backing target is a shared library, the
+plugin will be created as a module library. Where no backing target is
+provided or the plugin has no separate backing target, the plugin type can be
+specified with either the \c STATIC or \c SHARED keywords. If the plugin type
+is not determined by any of the preceding conditions, a static plugin will be
+created if Qt was built as static libraries, or a module library plugin
+otherwise.
+
+\c OUTPUT_DIRECTORY specifies the directory where the plugin library will be
+created. It should always be the same location as the QML module's
+\l{Module Definition qmldir Files}{qmldir} file. When \c OUTPUT_DIRECTORY is
+not given, it will be obtained from information stored on the
+\c BACKING_TARGET, where available. Note that this could be different to the
+directory of the backing target's own library. If an output directory cannot be
+obtained from the backing target, the \c CMAKE_CURRENT_BINARY_DIR is used by
+default.
+
+\c URI declares the module identifier of the QML module this plugin is
+associated with. The module identifier is the (dotted URI notation) identifier
+for the QML module. If \c URI is not given, a \c BACKING_TARGET must be
+provided and the backing target must have its URI recorded on it (typically by
+an earlier call to \l{qt6_add_qml_module}{qt_add_qml_module()}).
+
+Each plugin should have a C++ class that registers the module with the QML
+engine. By default, \c{qt_add_qml_plugin()} auto-generates the sources for this
+C++ class, and adds them to the \c{target}'s list of sources. The generated
+plugin class satisfies the requirements of the plugin being optional (see
+\l{Module Definition qmldir Files}). The class name is determined as follows:
+
+\list
+ \li If \c CLASS_NAME has been given, it will be used. It must match the name
+ used in the QML module's \c qmldir file.
+ \li If \c CLASS_NAME has not been given, but \c BACKING_TARGET has, the C++
+ class name will be taken from details recorded on that backing target.
+ Those details are usually recorded by an earlier call to
+ \l{qt_add_qml_module}{qt_add_qml_module()}, and they will match the name
+ used in the generated \c qmldir file. This is the recommended way to
+ provide the class name in most scenarios.
+ \li If the class name still cannot be determined, it is set to the module's
+ URI with dots replaced by underscores, and \c Plugin appended.
+\endlist
+
+Some plugins may require the plugin class to be written manually. For example,
+the plugin may need to perform additional initialization or register things
+not implemented by the default plugin class. In such cases, the
+\c NO_GENERATE_PLUGIN_SOURCE option can be given. You are then responsible for
+writing your own C++ plugin class and adding it to the \c target. Note that if
+you need to write your own plugin class, it is very unlikely that the plugin
+can be optional. This in turn means that the \c NO_PLUGIN_OPTIONAL keyword
+should be included in the call to \l{qt_add_qml_module}{qt_add_qml_module()}
+when defining the QML module, or else the generated \c qmldir file will be
+incorrect. Make sure your plugin class uses the same class name as determined
+from the logic just above.
+
*/
diff --git a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
index b2c31123fe..249ae57d79 100644
--- a/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
+++ b/src/qml/doc/src/cmake/qt_import_qml_plugins.qdoc
@@ -34,20 +34,44 @@
\brief Ensures QML plugins needed by a target are imported for static builds.
+\cmakecommandsince 6.0
+
\section1 Synopsis
\badcode
-qt_import_qml_plugins(...)
-
+qt_import_qml_plugins(target)
\endcode
-\versionlessNote qt6_import_qml_plugins()
+\versionlessCMakeCommandsNote qt6_import_qml_plugins()
\section1 Description
-TBD
+\note This command only has any effect if Qt was built statically. If called
+ using a non-static Qt, it will do nothing and return immediately.
+
+\c{qt_import_qml_plugins()} runs \c{qmlimportscanner} on the \c target
+immediately as part of the call. It finds the static QML plugins used by the
+\c target and links it to those plugins so that they are part of the executable
+or shared library that \c target represents. The search follows QML module
+imports recursively.
+
+Because the call to \c{qmlimportscanner} runs at configure time rather than
+generation or build time, \c{qt_import_qml_plugins()} only knows about the
+information recorded on the \c target (or other targets it links or imports)
+at the time \c{qt_import_qml_plugins()} is called. Any linking or import
+relationships added after this call will not be considered. Therefore, this
+command should be called as late as possible in the \c{target}'s directory
+scope so that all the linking and import relationships are known.
+
+If \c target was created using \l{qt6_add_executable}{qt_add_executable()},
+projects would not normally need to call \c{qt_import_qml_plugins()} directly.
+When Qt is built statically, the command is called automatically as part of
+\l{qt6_add_executable#Finalization}{target finalization} if \c target links to
+the Qml library. By default, this finalization occurs at the end of the same
+directory scope in which the \c target was created. If the \c target was
+created using the standard CMake \c{add_executable()} command instead, the
+project needs to call \c{qt_import_qml_plugins()} itself.
-\section1 Arguments
+\sa Q_IMPORT_QML_PLUGIN
-TBD
*/
diff --git a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
index 05770b30e0..dc1178be29 100644
--- a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
+++ b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
@@ -32,22 +32,197 @@
\title qt_target_qml_sources
\target qt6_target_qml_sources
-\brief Add qml sources to an existing QML module target.
+\brief Add qml files and resources to an existing QML module target.
+
+\cmakecommandsince 6.2
\section1 Synopsis
\badcode
-qt_target_qml_sources(...)
+qt_target_qml_sources(
+ target
+ [QML_FILES ...]
+ [RESOURCES ...]
+ [PREFIX resource_path]
+ [OUTPUT_TARGETS out_targets_var]
+ [NO_LINT]
+ [NO_CACHEGEN]
+ [NO_QMLDIR_TYPES]
+)
\endcode
-\versionlessNote qt6_target_qml_sources()
+\versionlessCMakeCommandsNote qt6_target_qml_sources()
\section1 Description
-TBD
+\note This command requires CMake 3.19 or later.
+
+\c{qt_target_qml_sources()} provides the ability to add more files to a QML
+module after \l{qt6_add_qml_module}{qt_add_qml_module()} has been called.
+Typically, you pass the set of \c{.qml} files and resources to
+\l{qt6_add_qml_module}{qt_add_qml_module()} directly, but in some cases, it may
+be desirable, or even necessary, to add files after
+\l{qt6_add_qml_module}{qt_add_qml_module()} has been called. For example, you
+may wish to add files conditionally based on an \c{if} statement expression,
+or from subdirectories that will only be added if certain criteria are met.
+You might want to add a set of files with different characteristics to the
+others, such as a different resource prefix, or with linting and bytecode
+compilation disabled. The \c{qt_target_qml_sources()} command enables these
+scenarios.
\section1 Arguments
-TBD
+The \c target must be the backing target of a QML module, or if the QML module
+has no separate backing target, it must be the module's plugin target.
+
+\c QML_FILES is a list of \c{.qml}, \c{.js} and \c{.mjs} files to be added to
+the QML module. This option has exactly the same effect as the \c QML_FILES
+option of the \l{qt6_add_qml_module}{qt_add_qml_module()} command, including
+the automatic compilation to bytecode and lint processing.
+
+The \c NO_CACHEGEN and \c NO_LINT options also have the same effect as they do
+for \l{qt6_add_qml_module}{qt_add_qml_module()}. They disable the bytecode
+compilation and lint processing for the files listed with \c QML_FILES. This
+behavior can also be specified just for individual files using
+\l{qml-source-file-properties}{source file properties}.
+
+\c NO_QMLDIR_TYPES prevents the \c QML_FILES from being added as types to the
+generated \l{qmldir-autogeneration}{qmldir} file.
+
+\c RESOURCES has exactly the same effect as the \c RESOURCES option of the
+\l{qt6_add_qml_module}{qt_add_qml_module()} command. It provides a list of
+files to be added to the \c target as ordinary resources. These files are
+typically things like images, shaders, etc. that the QML code refers to in some
+way.
+
+\target PREFIX
+Files added to the module via \c QML_FILES or \c RESOURCES will be placed under
+the same resource prefix and target path as they would if they were added by the
+\l{qt6_add_qml_module}{qt_add_qml_module()} command. This can be overridden by
+providing a different location with the \c PREFIX option. The value following
+the \c PREFIX keyword will be used directly, without appending any target path.
+The final resource path of each file will be the prefix, plus the path of the
+file below the \c CMAKE_CURRENT_SOURCE_DIR. The \l{QT_RESOURCE_ALIAS} source
+file property can also be used to override that relative path.
+
+\badcode
+qt_add_qml_module(backing
+ URI Example
+ VERSION 1.0
+ RESOURCE_PREFIX /my.company.com/imports
+)
+
+qt_target_qml_sources(backing
+ QML_FILES special/First.qml
+ RESOURCES icons/logo.png
+)
+
+qt_target_qml_sources(backing
+ PREFIX /other.company.com/debugging
+ QML_FILES Inspector.qml
+)
+\endcode
+
+In the above example, the \c backing target's resources will end up with the
+following contents:
+
+\list
+\li \c{/my.company.com/imports/Example/special/First.qml}
+\li \c{/my.company.com/imports/Example/icons/logo.png}
+\li \c{/other.company.com/debugging/Inspector.qml}
+\endlist
+
+\c OUTPUT_TARGETS is also analogous to the same option for
+\l{qt6_add_qml_module}{qt_add_qml_module()}. Use it to specify the name of a
+variable in which to store any additional targets created for static builds.
+If the \c target will be installed, these additional targets will also need to
+be installed to satisfy linking requirements.
+
+\target qml-source-file-properties
+\section1 Source File Properties
+
+A number of source file properties can be used to influence how each individual
+\c{.qml} file is treated at various points in the QML module processing. These
+override any higher level options specified in calls to
+\c{qt_target_qml_sources()} or \l{qt6_add_qml_module}{qt_add_qml_module()}.
+All of these properties need to be set before the files are added with either
+of those two commands.
+
+\c QT_QML_SKIP_QMLLINT can be set to \c TRUE on a source file to prevent it
+from being included in the \l{qmllint-auto}{automatic qmllint processing}.
+By default, all \c{.qml} files will be included in the target's lint run, but
+this option can be used to exclude specific files.
+
+\c QT_QML_SKIP_CACHEGEN does a similar thing, preventing a source file from
+being compiled to byte code when this property is set to \c TRUE. Note that the
+file will still be added to the \c target as a resource in uncompiled form
+(see \l{qmlcachegen-auto}{Caching compiled QML sources}).
+
+Set the \c QT_QML_SKIP_QMLDIR_ENTRY source file property to \c TRUE to prevent
+that \c{.qml} file from being added as a type to the QML module's typeinfo file
+(see \l{qmldir-autogeneration}{Auto-generating \c{qmldir} and typeinfo files}).
+This would normally only be used for a file that does not expose a public type,
+such as a private JS file.
+
+By default, when \l{qmldir-autogeneration}{generating the \c qmldir file}, a
+single type entry will be generated for each \c{.qml} file that provides a type.
+It will be given a version number \c{X.0} where \c{X} is the major version of
+the QML module. If the QML module has any \c PAST_MAJOR_VERSIONS set, the same
+pattern will be applied to those too, appending \c{X.0} for each past major
+version \c{X}. For situations where a file needs to provide type entries for
+a different set of versions instead (e.g. it was first added in a minor patch
+version after the \c{.0} release), specify those versions in the source file's
+\c QT_QML_SOURCE_VERSIONS property. One type entry will be created for each
+version.
+
+If the type that a \c{.qml} file provides is a singleton, set its
+\c QT_QML_SINGLETON_TYPE property to \c TRUE. Similarly, the file's
+\c QT_QML_INTERNAL_TYPE source property can be set to \c TRUE to indicate that
+the type it provides is an internal one. The name of the type itself can also
+be overridden using the \c QT_QML_SOURCE_TYPENAME property. All three of these
+will be reflected in the file's type entries in the
+\l{qmldir-autogeneration}{generated \c qmldir file}.
+
+\target QT_RESOURCE_ALIAS
+All files listed with \c QML_FILES or \c RESOURCES will be added to the
+\c{target}'s resources. Their location in the resources consists of a base point
+and a relative path. The base point defaults to the concatenation of the QML
+module's resource prefix and its target path, but these can be overridden with
+the \l PREFIX argument. The relative path will default to the path of the file
+relative to the \c{target}'s \c SOURCE_DIR target property. This relative path
+can be overridden by setting the \c QT_RESOURCE_ALIAS property on the source
+file. This is commonly used to collect files from different directories and
+have them appear in the resources under a common location.
+
+\target qt_target_qml_sources_example
+\snippet cmake/qt_target_qml_sources/CMakeLists.txt 0
+
+In the above example, the \c qt_target_qml_sources_example target's resources
+will end up with the following contents:
+
+\list
+\li \c{/my.company.com/imports/Example/File.qml}
+\li \c{/my.company.com/imports/Example/FunnySingleton.qml}
+\li \c{/my.company.com/imports/Example/templates/File.qml}
+\li \c{/my.company.com/imports/Example/some_old_thing.qml}
+\li \c{/my.company.com/imports/Example/button-types.png}
+\li \c{/my.company.com/imports/Example/doc/README.txt}
+\endlist
+
+The generated \c qmldir file will contain the following type entries:
+
+\badcode
+File 2.0 File.qml
+singleton FunnySingleton 2.0 FunnySingleton.qml
+OldThing 1.1 some_old_thing.qml
+OldThing 2.0 some_old_thing.qml
+\endcode
+
+\note The source FunnySingleton.qml file must already contain
+the \c {pragma Singleton} statement. Setting the \c QT_QML_SINGLETON_TYPE source
+property does not automatically generate the pragma.
+
+\snippet cmake/qt_target_qml_sources/FunnySingleton.qml 0
+
*/
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index 969bf4f7c0..d4fe08e37d 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -97,15 +97,18 @@ Types to QML} explains, the properties, methods and signals of any
QObject-derived class are accessible from QML code.
To register a QObject-derived class as an instantiable QML object type, add
-\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration and
+\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration. You
+also need to make adjustments in the build system. For qmake, add
\c {CONFIG += qmltypes}, a \c {QML_IMPORT_NAME}, and a
-\c QML_IMPORT_MAJOR_VERSION to your project file. This will register the class
-into the type namespace under the given major version, using either the class
-name or an explicitly given name as QML type name. The minor version(s) will
-be derived from any revisions attached to properties, methods, or signals. The
-default minor version is \c 0. You can explicitly restrict the type to be
-available only from specific minor versions by adding the
-\c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
+\c QML_IMPORT_MAJOR_VERSION to your project file. For CMake, the file containing
+the class should be part of a target set-up with
+\l{qt_add_qml_module}{qt_add_qml_module()}.
+This will register the class into the type namespace under the given major version,
+using either the class name or an explicitly given name as QML type name. The
+minor version(s) will be derived from any revisions attached to properties,
+methods, or signals. The default minor version is \c 0. You can explicitly
+restrict the type to be available only from specific minor versions by adding
+the \c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
import suitable versions of the namespace in order to use the type.
For example, suppose there is a \c Message class with \c author and
@@ -127,26 +130,52 @@ This type can be registered by adding an appropriate type namespace and version
number to the project file. For example, to make the type available in the
\c com.mycompany.messaging namespace with version 1.0:
-\code
-CONFIG += qmltypes
-QML_IMPORT_NAME = com.mycompany.messaging
-QML_IMPORT_MAJOR_VERSION = 1
-\endcode
+\if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{selected}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section3 Using CMake
+\endif
+ \badcode
+ qt_add_qml_module(messaging
+ URI com.mycompany.messaging
+ VERSION 1.0
+ SOURCES
+ message.cpp message.h
+ )
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+\else
+ \section3 Using QMake
+\endif
+ \code
+ CONFIG += qmltypes
+ QML_IMPORT_NAME = com.mycompany.messaging
+ QML_IMPORT_MAJOR_VERSION = 1
+ \endcode
+
+ If the header the class is declared in is not accessible from your project's
+ include path, you may have to amend the include path so that the generated
+ registration code can be compiled.
+
+ \code
+ INCLUDEPATH += com/mycompany/messaging
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+\endif
-If the header the class is declared in is not accessible from your project's
-include path, you may have to amend the include path so that the generated
-registration code can be compiled:
-\code
-INCLUDEPATH += com/mycompany/messaging
-\endcode
The type can be used in an \l{qtqml-syntax-basics.html#object-declarations}
{object declaration} from QML, and its properties can be read and written to,
as per the example below:
\qml
-import com.mycompany.messaging 1.0
+import com.mycompany.messaging
Message {
author: "Amelie"
@@ -334,10 +363,9 @@ classes directly, if this is either not possible or is complicated by some
other concerns, extension objects allow limited extension possibilities
without direct modifications.
-\e{Extension objects} add additional properties to an existing type. Extension
-objects can only add properties, not signals or methods. An extended type
-definition allows the programmer to supply an additional type, known as the
-\e{extension type}, when registering the class. The properties are transparently
+\e{Extension objects} add additional properties to an existing type. An extended
+type definition allows the programmer to supply an additional type, known as the
+\e{extension type}, when registering the class. Its members are transparently
merged with the original target class when used from within QML. For example:
\snippet referenceexamples/extended/example.qml 0
@@ -619,6 +647,8 @@ Item {
}
\endqml
+This is commonly referred to as "on" syntax.
+
Clients can register their own property value source types, but currently not
property value write interceptors.
diff --git a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
index 6e6d067ba6..b5f0676920 100644
--- a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
+++ b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -397,13 +397,11 @@ plugin with the Qt meta object system.
Here is the \c ChartsPlugin definition in \c chartsplugin.h:
-\snippet tutorials/extending-qml/chapter6-plugins/import/chartsplugin.h 0
+\snippet tutorials/extending-qml/chapter6-plugins/Charts/chartsplugin.h 0
-Then, we write a \c .pro project file that defines the project as a plugin library
-and specifies with DESTDIR that library files should be built into a \c {../Charts}
-directory.
+Then, we write a \c .pro project file that defines the project as a plugin library.
-\quotefile tutorials/extending-qml/chapter6-plugins/import/import.pro
+\quotefile tutorials/extending-qml/chapter6-plugins/Charts/Charts.pro
When building this example on Windows or Linux, the \c Charts directory will be
located at the same level as the application that uses our new import module.
@@ -413,7 +411,7 @@ plugin binary is copied to \c Contents/PlugIns in the the application bundle;
this path is set in \c {chapter6-plugins/app.pro}:
\quotefromfile tutorials/extending-qml/chapter6-plugins/app.pro
-\skipto osx
+\skipto macos
\printuntil }
To account for this, we also need to add this location as a
@@ -432,16 +430,16 @@ to the same location as the plugin binary.
The \c qmldir file declares the module name and the plugin that is made available
by the module:
-\quotefile tutorials/extending-qml/chapter6-plugins/import/qmldir
+\quotefile tutorials/extending-qml/chapter6-plugins/Charts/qmldir
Now we have a QML module that can be imported to any application, provided that the
QML engine knows where to find it. The example contains an executable that loads
\c app.qml, which uses the \c {import Charts 1.0} statement. Alternatively, you can
-load the QML file using the \l{Prototyping with qmlscene}{qmlscene tool}, setting the
-import path to the current directory so that it finds the \c qmldir file:
+load the QML file using the \l {Prototyping with the QML Runtime Tool}{qml tool},
+setting the import path to the current directory so that it finds the \c qmldir file:
\code
- qmlscene -I . app.qml
+ qml -I . app.qml
\endcode
The module "Charts" will be loaded by the QML engine, and the types provided by that
diff --git a/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
new file mode 100644
index 0000000000..64342fe1f8
--- /dev/null
+++ b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+\page qtqml-integrating-with-js-values-from-cpp.html
+\title Integrating with JavaScript values from C++
+\brief Description of how to load and access JavaScript from C++ code.
+
+The following classes can be used to load and access JavaSript from C++ code:
+
+\list
+
+ \li \l QJSValue, which acts as a container for Qt/JavaScript data types.
+ \li \l QJSManagedValue, which represents a value on the JavaScript heap
+ belonging to a \l QJSEngine.
+ \li \l QJSPrimitiveValue, which operates on primitive types in JavaScript semantics.
+\endlist
+
+Use QJSValue to transfer values to and from the engine, and use QJSManagedValue
+to interact with JavaScript values. Only use QJSPrimitiveValues if you have to
+emulate the semantics of JS primitive values in C++.
+
+\table
+ \header
+ \li QJSValue
+ \li QJSManagedValue
+ \li QJSPrimitiveValue
+ \row
+ \li Persistently store values
+ \li Short lived
+ \li Short lived
+ \row
+ \li Transport values to/from engine
+ \li Access properties
+ \li Only Primitives
+ \row
+ \li
+ \li Call methods
+ \li Basic arithmetic and comparison
+\endtable
+
+\section1 QJSValue as a Container Type
+
+\l QJSValue stores the Qt/JavaScript data types supported in ECMAScript including
+function, array and arbitrary object types as well as anything supported by
+QVariant. As a container, it can be used to pass values to and receive values
+from a QJSEngine.
+
+\snippet qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp qjs-as-container
+
+In case of a cache miss, \c undefined is returned. Otherwise, the cached value is
+returned. Note that implicit conversions (from QString and QJSValue::SpecialValue respectively)
+occur when the value is returned.
+
+QJSValue also has an API to interact with the contained value, but using
+QJSManagedValue is recommended.
+
+\section1 Primitive and Managed Values
+
+QJSValue and QJSManagedValue store values that can be either managed or primitive.
+In QML’s JS engine, a managed value can be thought of as a pointer to some data
+structure on the heap, whose memory is managed by the engine’s garbage collector.
+The actual content of primitive values is stored directly, using a technique
+called NaN-boxing that enables you to represent a NaN-value in multiple ways, even
+though only two are actually needed; one for signalling and one for quiet NaN-value.
+
+\table
+\header
+ \li Primitive Values
+ \li Managed Values
+\row
+ \li int
+ \li Function
+\row
+ \li double
+ \li Array
+\row
+ \li undefined
+ \li QVariant
+\row
+ \li null
+ \li string object
+\row
+ \li QString
+ \li
+\endtable
+
+A pointer to the engine can be obtained from a managed value, but not from a
+primitive one. When using QJSValue for its JavaScript API, you need access
+to the engine to evaluate JavaScript. For example, to run the \c call(args) function,
+you have to interpret it in the engine. This works, as the function is a managed
+value, and you can obtain the engine from it.
+
+Similarly, where the engine is needed when you call a function or
+access a property on a primitive number or string. Whenever you call a method on
+a primitive, an instance of its corresponding non-primitive objects is created.
+This is referred as boxing. When you write \c (42).constructor, that is equivalent
+to \c (new Number(42)).constructor, and it returns the constructor method of the
+global number object. Accordingly, if you write \c QJSValue(42).property("constructor"),
+you would expect to obtain a QJSValue containing that function. However, what you
+get is instead a QJSValue containing \c undefined.
+
+The QJSValue that you constructed contains only a primitive value, and thus you have
+no way to access the engine. You also can’t simply hardcode the property lookup
+for primitive values in QJSEngine, as in one engine you might set
+\e {Number.prototype.constructor.additionalProperty = "the Spanish Inquisition"}
+whereas in another \e {Number.prototype.constructor.additionalProperty = 42}.
+The end result would then clearly be unexpected.
+
+To ensure that property accesses always work, you would need to always store boxed
+values in QJSValue or store an additional pointer to the engine.
+
+However, this would be incompatible with how QJSValue is currently used, lead to
+pointless JS heap allocations when passing around primitives, and increase the
+size needed to store a QJSValue. Therefore, you should use \l QJSValue only for
+storage and \l QJSManagedValue to obtain the engine.
+
+\section1 QJSManagedValue
+
+QJSManagedValue is similar to QJSValue, with a few differences:
+
+\list
+\li The constructors (except for the default and move constructor2) require
+ passing a QJSEngine pointer.
+\li Methods like \c deleteProperty and \l isSymbol are added.
+\li If QJSManagedValue methods encounter an exception, they leave it intact.
+\endlist
+
+To obtain the engine in code, either you are in a scripting context where you’ve
+already got access to an engine to create new objects with \c QJSEngine::newObject
+and to evaluate expressions with \c QJSEngine::evaluate, or you want to evaluate
+some JavaScript in a QObject that has been registered with the engine. In the
+latter case, you can use \c qjsEngine(this) to obtain the currently active
+QJSEngine.
+
+QJSManagedValue also provides a few methods that have no equivalent in QJSEngine.
+
+In the example below, QJSManagedValue methods encounter an exception, and
+QJSEngine::catchError is used to handle the exception.
+
+\snippet qtjavascript/integratingjswithcpp/exampleqjsengine.cpp qjs-engine-example
+
+However, inside a method of a registered object, you might want to instead let
+the exception bubble up the call stack.
+
+QJSManagedValue should be temporarily created on the stack,
+and discarded once you don’t need to work any longer on the contained value.
+Since QJSValue can store primitive values in a more efficient way, QJSManagedValue
+should also not be used as an interface type which is the return or parameter type of
+functions, and the type of properties, as the engine does not treat it in a
+special way, and will not convert values to it (in contrast to QJSValue).
+
+\section1 QJSPrimitiveValue
+
+\l QJSPrimitiveValue can store any of the primitive types, and supports arithmetic
+operations and comparisons according to the ECMA-262 standard. It allows for
+low-overhead operations on primitives in contrast to QJSManagedValue, which always goes
+through the engine, while still yielding results that are indistinguishable
+from what the engine would return. As QJSPrimitiveValue is comparatively large, it
+is not recommended to store values.
+
+*/
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index 0a824bb5b5..6277b01af3 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -105,6 +105,88 @@ You can also connect to any signals or call methods defined in the component
using QMetaObject::invokeMethod() and QObject::connect(). See \l {Invoking QML Methods}
and \l {Connecting to QML Signals} below for further details.
+\section1 Accessing QML Objects via well-defined C++ Interfaces
+
+The best way of interacting with QML from C++ is to define an interface for
+doing so in C++ and accessing it in QML itself. With other methods, refactoring
+your QML code can easily lead to your QML / C++ interaction breaking. It also
+helps to reason about the interaction of QML and C++ code, as having it driven
+via QML can be more easily reasoned about by both users and tooling such as
+qmllint. Accessing QML from C++ will lead to QML code that cannot be understood
+without manually verifying that no outside C++ code is modifying a given QML
+component, and even then the extent of the access might change over time, making
+continued use of this strategy a maintenance burden.
+
+To let QML drive the interaction, first you need to define a C++ interface:
+
+\code
+class CppInterface : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ // ...
+};
+\endcode
+
+Using a QML-driven approach, this interface can be interacted with in two ways:
+
+\section2 Singletons
+
+One option is to register the interface as a singleton by adding the \l
+QML_SINGLETON macro to the interface, exposing it to all components. Following
+that, the interface becomes available via a simple import statement:
+
+\code
+import my.company.module
+
+Item {
+ Component.onCompleted: {
+ CppInterface.foo();
+ }
+}
+\endcode
+
+Use this approach if you need your interface in more places than the root component, as
+simply passing down an object would require explicitly passing it on to other
+components via a property or utilizing the slow and not recommended method of
+using \l {Unqualified access}{unqualified access}.
+
+\section2 Initial properties
+
+Another option is to mark the interface as uncreatable via \l QML_UNCREATABLE
+and supplying it to the root QML Component by using \l
+QQmlComponent::createWithInitialProperties() and a \l {Required
+Properties}{required property} on the QML end.
+
+Your root component may look something like this:
+
+\code
+import QtQuick
+
+Item {
+ required property CppInterface interface
+ Component.onCompleted: {
+ interface.foo();
+ }
+}
+\endcode
+
+Marking the property as required here protects the component against being
+created without the interface property being set.
+
+You can then initialize your component in the same way as outlined in \l
+{Loading QML Objects from C++} except using \c {createWithInitialProperties()}:
+
+\code
+ component.createWithInitialProperties(QVariantMap{{u"interface"_qs, QVariant::fromValue<CppInterface *>(new CppInterface)}});
+\endcode
+
+This method is to be preferred if you know that your interface only needs to be
+available to the root component. It also allows for connecting to signals and
+slots of the interface more easily on the C++ side.
+
+If neither of these methods suit your needs you may want to investigate the usage of
+\l {Using C++ Models with Qt Quick Views}{C++ models} instead.
\section1 Accessing Loaded QML Objects by Object Name
@@ -189,9 +271,9 @@ Notice the parameter and return type specified after the colon. You can use \l
{QML Basic Types}{basic types} and \l {QML Object Types}{object types} as type
names.
-If the type is omitted in QML, then you must specify QVariant as type with
-Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod.
-
+If the type is omitted or specified as \c var in QML, then you must pass
+QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
+QMetaObject::invokeMethod.
\section2 Connecting to QML Signals
diff --git a/src/qml/doc/src/javascript/finetuning.qdoc b/src/qml/doc/src/javascript/finetuning.qdoc
index 97dc2421c5..61ac7d0c8f 100644
--- a/src/qml/doc/src/javascript/finetuning.qdoc
+++ b/src/qml/doc/src/javascript/finetuning.qdoc
@@ -82,9 +82,12 @@ Running JavaScript code can be influenced by a few environment variables, partic
\li \c{QV4_MAX_CALL_DEPTH}
\li Stack overflows when running (as opposed to compiling) JavaScript are prevented by
controlling the call depth: the number of nested function invocations. By
- default, an exception is generated if the call depth exceeds 1234. If it contains a
- number, this environment variable overrides the maximum call depth. Beware that the
- recursion limit when compiling JavaScript is not affected.
+ default, an exception is generated if the call depth exceeds a maximum number tuned
+ to the platform's default stack size. If the \c{QV4_MAX_CALL_DEPTH} environment
+ variable contains a number, this number is used as maximum call depth. Beware that
+ the recursion limit when compiling JavaScript is not affected. The default maximum
+ call depth is 1234 on most platforms. On QNX it is 640 because on QNX the default
+ stack size is smaller than on most platforms.
\row
\li \c{QV4_MM_AGGRESSIVE_GC}
\li Setting this environment variable runs the garbage collector before each memory
diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc
index 6f1134a3e2..943da5cbef 100644
--- a/src/qml/doc/src/javascript/hostenvironment.qdoc
+++ b/src/qml/doc/src/javascript/hostenvironment.qdoc
@@ -42,7 +42,8 @@ Like a browser or server-side JavaScript environment, the QML runtime implements
all of the built-in types and functions defined by the standard, such as Object, Array, and Math.
The QML runtime implements the 7th edition of the standard.
-\l{Nullish Coalescing} (since Qt 5.15) and \l{Optional Chaining} (since Qt 6.2) are also implemented in the QML runtime.
+\l{Nullish Coalescing} (\c{??}) (since Qt 5.15) and \l{Optional Chaining} (\c{?.}) (since Qt 6.2)
+are also implemented in the QML runtime.
The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more
information on their use, please refer to the ECMA-262 7th edition standard or one of the many online
@@ -70,11 +71,10 @@ to use from C++. See
\l {qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}
for more information.
-Type assertions can also be used in order to cast an object to a different
-object type. If the object is actually of the given type, then the type
-assertion returns the same object. If not, it returns \c null. In the following
-snippet we assert that the \c parent object is a \c Rectangle before accessing
-a specific member of it.
+Type assertions (sometimes called \e as-casts) can also be used in order to cast an object to a
+different object type. If the object is actually of the given type, then the type assertion returns
+the same object. If not, it returns \c null. In the following snippet we assert that the \c parent
+object is a \c Rectangle before accessing a specific member of it.
\qml
Item {
diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc
index 9227f0e604..8d49c02f62 100644
--- a/src/qml/doc/src/javascript/imports.qdoc
+++ b/src/qml/doc/src/javascript/imports.qdoc
@@ -95,17 +95,18 @@ or modules).
A JavaScript resource may import another in the following fashion:
\code
-.import "filename.js" as Qualifier
+import * as MathFunctions from "factorial.mjs";
\endcode
-For example:
+Or:
\code
-import * as MathFunctions from "factorial.mjs";
+.import "filename.js" as Qualifier
\endcode
-The latter is standard ECMAScript syntax for importing ECMAScript modules, and
+The former is standard ECMAScript syntax for importing ECMAScript modules, and
only works from within ECMAScript modules as denoted by the \c mjs file
-extension. The former is an extension to JavaScript provided by the \c QML
-engine and will work also with non-modules.
+extension. The latter is an extension to JavaScript provided by the \c QML
+engine and will work also with non-modules. As an extension superseded by the
+ECMAScript standard, its usage is discouraged.
When a JavaScript file is imported this way, it is imported with a qualifier.
The functions in that file are then accessible from the importing script via the
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 3fc4c86f74..cc4f4a2747 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -45,8 +45,8 @@
\endcode
You can use the build system to register the type in the type namespace
- \e {com.mycompany.qmlcomponents} with major version \c 1 by specifying the
- following in your project file:
+ \e {com.mycompany.qmlcomponents} with major version \c 1.
+ For qmake, specify the following in your project file:
\badcode
CONFIG += qmltypes
@@ -54,6 +54,15 @@
QML_IMPORT_MAJOR_VERSION = 1
\endcode
+ With CMake, you pass the URI and version to qt_add_qml_module
+
+ \badcode
+ qt6_add_qml_module(myapp
+ URI com.mycompany.qmlcomponents
+ VERSION 1.0
+ )
+ \endcode
+
Once registered, the type can be used in QML by importing the
same type namespace and version number:
@@ -101,8 +110,9 @@
\relates QQmlEngine
Declares the enclosing type to be available, but anonymous in QML. The type
- cannot be created or used as property type, but when passed from C++, it is
- recognized.
+ cannot be created or used to declare properties in QML, but when passed from
+ C++, it is recognized. In QML, you can use properties of this type if they
+ are declared in C++.
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_UNCREATABLE(), QML_INTERFACE
*/
@@ -274,7 +284,7 @@
// Initialize this using myObject where you would previously
// call qmlRegisterSingletonInstance().
- static MySingleton *s_singletonInstance = nullptr;
+ inline static MySingleton *s_singletonInstance = nullptr;
static MySingleton *create(QQmlEngine *, QJSEngine *engine)
{
@@ -298,7 +308,7 @@
}
private:
- static QJSEngine *s_engine = nullptr;
+ inline static QJSEngine *s_engine = nullptr;
};
\endcode
@@ -1243,20 +1253,25 @@
\fn bool qmlProtectModule(const char* uri, int majVersion);
\relates QQmlEngine
- This function protects a module from having types registered into it. This
- can be used to prevent other plugins from injecting types into your module.
- It can also be a performance improvement, as it allows the engine to skip
- checking for the possibility of new types or plugins when this import is
- reached.
-
- The performance benefit is primarily seen when registering application
- specific types from within the application instead of through a plugin.
- Using qmlProtectModule allows the engine to skip checking for a plugin when
- that uri is imported, which can be noticeable with slow file systems.
-
- After this function is called, any attempt to register C++ types into this
- uri, major version combination will lead to a runtime error. Call this after
- you have registered all of your types with the engine.
+ This function protects a module from further modification. This can be used
+ to prevent other plugins from injecting types into your module. It can also
+ be a performance improvement, as it allows the engine to skip checking for
+ the possibility of new types or plugins when this import is reached.
+
+ Once qmlProtectModule has been called, a QML engine will not search for a new
+ \c qmldir file to load the module anymore. It will re-use any \c qmldir files
+ it has loaded before, though. Therefore, types present at this point continue
+ to work. Mind that different QML engines may load different modules. The
+ module protection, however, is global and affects all engines. The overhead
+ of locating \c qmldir files and loading plugins may be noticeable with slow file
+ systems. Therefore, protecting a module once you are sure you won't need to
+ load it anymore can be a good optimization. Mind also that the module lock
+ not only affects plugins but also any other qmldir directives, like \c import
+ or \c prefer, as well as any composite types or scripts declared in a \c qmldir
+ file.
+
+ In addition, after this function is called, any attempt to register C++ types
+ into this uri, major version combination will lead to a runtime error.
Returns true if the module with \a uri as a \l{Identified Modules}
{module identifier} and \a majVersion as a major version number was found
diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
index 718b0c25ac..562b262271 100644
--- a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
@@ -53,8 +53,12 @@ The type name has the following requirements:
This document is then automatically recognized by the engine as a definition of
a QML type. Additionally, a type defined in this manner is automatically made
-available to other QML files within the same directory as the engine searches
-within the immediate directory when resolving QML type names.
+available to other QML files within the same local directory as the engine
+searches within the immediate directory when resolving QML type names.
+
+\note The QML engine does not automatically search remote directories this way.
+You have to add a qmldir file if your documents are loaded over the network. See
+\l{Importing QML Document Directories}.
\section2 Custom QML Type Definition
diff --git a/src/qml/doc/src/qmllanguageref/documents/networktransparency.qdoc b/src/qml/doc/src/qmllanguageref/documents/networktransparency.qdoc
index d762621d6c..8f21a7348c 100644
--- a/src/qml/doc/src/qmllanguageref/documents/networktransparency.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/networktransparency.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -51,7 +51,8 @@ Image {
Network transparency is supported throughout QML, for example, both the FontLoader
and Image elements support loading resources from a remote server.
-Even QML types themselves can be on the network - if the \l {Prototyping with qmlscene} is used to load
+Even QML types themselves can be on the network: if the
+\l {Prototyping with the QML Runtime Tool}{qml tool} is used to load
\tt http://example.com/mystuff/Hello.qml and that content refers to a type "World", the engine
will load \tt http://example.com/mystuff/qmldir and resolve the type just as it would for a local file.
For example if the qmldir file contains the line "World World.qml", it will load
diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
index 303ed6b18c..493b031b60 100644
--- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
@@ -35,8 +35,8 @@ specified by the module in its \c qmldir file. This enables such modules to
be imported with a unique identifier that remains the same no matter where the
module is located on the local file system.
-When importing an identified module, an unquoted identifier is used, with a
-mandatory version number:
+When importing an identified module, an unquoted identifier is used, with an
+optional version number:
\snippet qml/imports/installed-module.qml imports
@@ -44,6 +44,12 @@ Identified modules must be installed into the
\l{qtqml-syntax-imports.html#qml-import-path}{import path} in order to be found
by the QML engine.
+Syntactically, each dot-separated segment of the URI must be a well-formed
+ECMAScript Identifier Name. This means, for example, the segments must not start
+with a number and they must not contain \e{-} (minus) characters. As the URI
+will be translated into directory names, you should restrict it to alphanumeric
+characters of the latin alphabet, underscores, and dots.
+
\section1 Locally Installed Identified Modules
A directory of QML and/or C++ files can be shared as an identified module if it
diff --git a/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc b/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
index 95e8a179de..4d7ea272ce 100644
--- a/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
@@ -43,278 +43,287 @@ module. For more information about the first form of \c qmldir file, see
\section1 Contents of a Module Definition qmldir File
-A \c qmldir file is a plain-text file that contains
-the following commands:
-
-\table 70%
- \header
- \li Syntax
- \li Usage
- \row
- \li
- \code
-module <ModuleIdentifier>
- \endcode
- \li Declares the module identifier of the module.
- The <ModuleIdentifier> is the (dotted URI notation) identifier
- for the module, which must match the module's install path.
-
- The \l{Identified Modules#Semantics of Identified Modules}
- {module identifier directive} must be the first line of the file.
- Exactly one module identifier directive may exist in the \c qmldir
- file.
-
- Example:
- \code
-module ExampleModule
- \endcode
-
- \row
- \li
- \code
-[singleton] <TypeName> <InitialVersion> <File>
- \endcode
- \li Declares a \l{qtqml-typesystem-objecttypes.html}{QML object type}
- to be made available by the module.
- \list
- \li \c [singleton] Optional. Used to declare a singleton type.
- \li \c <TypeName> is the type being made available
- \li \c <InitialVersion> is the module version for which the type is to be made available
- \li \c <File> is the (relative) file name of the QML file that defines the type
- \endlist
-
- Zero or more object type declarations may exist in the \c qmldir
- file, however each object type must have a unique type name within
- any particular version of the module.
- \note To declare a \c singleton type, the QML file defining the
- type must include the \c {pragma Singleton} statement.
-
- Example:
- \code
-//Style.qml with custom singleton type definition
-pragma Singleton
-import QtQuick 2.0
+A \c qmldir file is a plain-text file that contains the following commands:
-QtObject {
- property int textSize: 20
- property color textColor: "green"
-}
+\list
+ \li \l {Module Identifier Declaration}
+ \li \l {Object Type Declaration}
+ \li \l {Internal Object Type Declaration}
+ \li \l {JavaScript Resource Declaration}
+ \li \l {Plugin Declaration}
+ \li \l {Plugin Classname Declaration}
+ \li \l {Type Description File Declaration}
+ \li \l {Module Dependencies Declaration}
+ \li \l {Module Import Declaration}
+ \li \l {Designer Support Declaration}
+ \li \l {Preferred Path Declaration}
+\endlist
-// qmldir declaring the singleton type
-module CustomStyles
-singleton Style 1.0 Style.qml
+\note Each command in a \c qmldir file must be on a separate line.
-// singleton type in use
-import QtQuick 2.0
-import CustomStyles 1.0
+In addition to commands, you can also add comments, which are lines starting
+with \c {#}.
-Text {
- font.pixelSize: Style.textSize
- color: Style.textColor
- text: "Hello World"
-}
- \endcode
-
- \row
- \li
- \code
-internal <TypeName> <File>
- \endcode
- \li Declares an object type that is in the module but should not be
- made available to users of the module.
-
- Zero or more internal object type declarations may exist in the
- \c qmldir file.
-
- Example:
- \code
-internal MyPrivateType MyPrivateType.qml
- \endcode
-
- This is necessary if the module may be imported remotely (see
- \l{Identified Modules#Remotely Installed Identified Modules}
- {Remotely Installed Identified Modules}) because if an exported type depends
- on an non-exported type within the module, the engine must also
- load the non-exported type.
-
- \row
- \li
- \code
-<ResourceIdentifier> <InitialVersion> <File>
- \endcode
- \li Declares a JavaScript file to be made available by the module.
- The resource will be made available via the specified identifier
- with the specified version number.
-
- Zero or more JavaScript resource declarations may exist in the
- \c qmldir file, however each JavaScript resource must have a unique
- identifier within any particular version of the module.
-
- Example:
- \code
-MyScript 1.0 MyScript.js
- \endcode
-
- See the documentation about \l{qtqml-javascript-resources.html}
- {defining JavaScript resources} and
- \l{qtqml-javascript-imports.html}
- {Importing JavaScript Resources In QML} for more information.
-
- \row
- \li
- \code
-[optional] plugin <Name> [<Path>]
- \endcode
- \li Declares a plugin to be made available by the module.
-
- \list
- \li \c optional denotes that the plugin itself does not contain
- any relevant code and only serves to load a library it links
- to. If given, and if any types for the module are already
- available, indicating that the library has been loaded by some
- other means, QML will not load the plugin.
- \li \c <Name> is the plugin library name. This is usually not the
- same as the file name of the plugin binary, which is platform
- dependent; e.g. the library \c MyAppTypes would produce
- \c libMyAppTypes.so on Linux and \c MyAppTypes.dll on Windows.
- \li \c <Path> (optional) specifies either:
- \list
- \li an absolute path to the directory containing the plugin
- file, or
- \li a relative path from the directory containing the \c qmldir
- file to the directory containing the plugin file.
- \endlist
-
- By default the engine searches for the plugin library in the
- directory that contains the \c qmldir file. (The plugin search
- path can be queried with QQmlEngine::pluginPathList() and
- modified using QQmlEngine::addPluginPath().)
- \endlist
-
- Zero or more C++ plugin declarations may exist in the \c qmldir
- file, however since plugin loading is a relatively expensive
- operation, clients are advised to specify at most a single plugin.
-
- Example:
- \code
-plugin MyPluginLibrary
- \endcode
- \row
- \li
- \code
-classname <C++ plugin class>
- \endcode
- \li Provides the class name of the C++ plugin used by the module.
-
- This information is required for all the QML modules that depend
- on a C++ plugin for additional functionality. Qt Quick applications
- built with static linking cannot resolve the module imports without
- this information.
-
- \row
- \li
- \code
-typeinfo <File>
- \endcode
- \li Declares a \l{Type Description Files}{type description file} for
- the module that can be read by QML tools such as Qt Creator to
- access information about the types defined by the module's plugins.
- \c <File> is the (relative) file name of a \c .qmltypes file.
-
- Example:
- \code
-typeinfo mymodule.qmltypes
- \endcode
-
- Without such a file, QML tools may be unable to offer features such
- as code completion for the types defined in your plugins.
-
- \row
- \li
- \code
-depends <ModuleIdentifier> <InitialVersion>
- \endcode
- \li Declares that this module depends on another.
-
- Example:
- \code
-depends MyOtherModule 1.0
- \endcode
-
- This declaration is necessary only in cases when the dependency is
- hidden: for example, when the C++ code for one module is used to
- load QML (perhaps conditionally) which then depends on other
- modules. In such cases, the \c depends declaration is necessary
- to include the other modules in application packages.
- \row
- \li
- \code
-import <ModuleIdentifier> [<Version>]
- \endcode
- \li Declares that this module imports another.
-
- Example:
- \code
-import MyOtherModule 1.0
- \endcode
-
- The types from the other module are made available in the same type
- namespace as this module is imported into. Omitting the version
- imports the latest version available of the other module, specifying
- \c auto as version imports the same version as the version of this
- module specified in the QML \c import statement.
-
- \row
- \li
- \code
-# <Comment>
- \endcode
- \li Declares a comment. These are ignored by the engine.
-
- Example:
- \code
-# this is a comment
- \endcode
-
- \row
- \li
- \code
-designersupported
- \endcode
-
- \li Set this property if the plugin is supported by Qt Quick Designer.
- By default, the plugin will not be supported.
-
- A plugin that is supported by Qt Quick Designer has to be properly
- tested. This means that the plugin does not crash when running inside
- the qml2puppet that is used by Qt Quick Designer to execute QML.
- Generally the plugin should work well in the Qt Quick Designer
- and not cause any show stoppers, like taking huge amounts of memory,
- slowing down the qml2puppet heavily or anything else that renders
- the plugin effectively unusable in the Qt Quick Designer.
-
- The items of an unsupported plugin are not painted in the Qt Quick Designer,
- but they are still available as empty boxes and the properties can be edited.
-
- \row
- \li
- \code
-prefer <Path>
- \endcode
-
- \li This property directs the QML engine to load any further files for this
- module from <path>, rather than the current directory. This can be used
- to load files compiled with qmlcachegen.
-
- For example, you can add a module's QML files as resources to a resource
- path \c{:/my/path/MyModule/}. Then, add \c{prefer :/my/path/MyModule} to
- the qmldir file in order to use the files in the resource system, rather
- than the ones in the file system. If you then use qmlcachegen for those,
- the pre-compiled files will be available to any clients of the module.
-
-\endtable
-
-Each command in a \c qmldir file must be on a separate line.
+\section2 Module Identifier Declaration
+
+\code
+ module <ModuleIdentifier>
+\endcode
+
+Declares the module identifier of the module. The <ModuleIdentifier> is the
+(dotted URI notation) identifier for the module, which must match the module's
+install path.
+
+The \l{Identified Modules#Semantics of Identified Modules}
+{module identifier directive} must be the first line of the file. Exactly one
+module identifier directive may exist in the \c qmldir file.
+
+Example:
+\code
+ module ExampleModule
+\endcode
+
+\section2 Object Type Declaration
+\code
+ [singleton] <TypeName> <InitialVersion> <File>
+\endcode
+
+Declares a \l{qtqml-typesystem-objecttypes.html}{QML object type}
+to be made available by the module.
+\list
+ \li \c [singleton] Optional. Used to declare a singleton type.
+ \li \c <TypeName> is the type being made available
+ \li \c <InitialVersion> is the module version for which the type is to be
+ made available
+ \li \c <File> is the (relative) file name of the QML file that defines
+ the type
+\endlist
+
+Zero or more object type declarations may exist in the \c qmldir
+file. However, each object type must have a unique type name within
+any particular version of the module.
+\note To declare a \c singleton type, the QML file defining the
+type must include the \c {pragma Singleton} statement.
+
+Example:
+\code
+ //Style.qml with custom singleton type definition
+ pragma Singleton
+ import QtQuick 2.0
+
+ QtObject {
+ property int textSize: 20
+ property color textColor: "green"
+ }
+
+ // qmldir declaring the singleton type
+ module CustomStyles
+ singleton Style 1.0 Style.qml
+
+ // singleton type in use
+ import QtQuick 2.0
+ import CustomStyles 1.0
+
+ Text {
+ font.pixelSize: Style.textSize
+ color: Style.textColor
+ text: "Hello World"
+ }
+\endcode
+
+\section2 Internal Object Type Declaration
+
+\code
+ internal <TypeName> <File>
+\endcode
+
+Declares an object type that is in the module but should not be
+made available to users of the module.
+
+Zero or more internal object type declarations may exist in the
+\c qmldir file.
+
+Example:
+\code
+ internal MyPrivateType MyPrivateType.qml
+\endcode
+
+This is necessary if the module is imported remotely
+(see \l{Identified Modules#Remotely Installed Identified Modules}
+{Remotely Installed Identified Modules}) because if an exported type depends
+on a non-exported type within the module, the engine must also
+load the non-exported type.
+
+\section2 JavaScript Resource Declaration
+
+\code
+ <ResourceIdentifier> <InitialVersion> <File>
+\endcode
+
+Declares a JavaScript file to be made available by the module.
+The resource will be made available via the specified identifier
+with the specified version number.
+
+Zero or more JavaScript resource declarations may exist in the
+\c qmldir file. However, each JavaScript resource must have a unique
+identifier within any particular version of the module.
+
+Example:
+\code
+ MyScript 1.0 MyScript.js
+\endcode
+
+See the documentation about \l{qtqml-javascript-resources.html}
+{defining JavaScript resources} and
+\l{qtqml-javascript-imports.html}
+{Importing JavaScript Resources In QML} for more information.
+
+\section2 Plugin Declaration
+
+\code
+ [optional] plugin <Name> [<Path>]
+\endcode
+
+Declares a plugin to be made available by the module.
+
+\list
+ \li \c optional denotes that the plugin itself does not contain
+ any relevant code and only serves to load a library it links
+ to. If given, and if any types for the module are already
+ available, indicating that the library has been loaded by some
+ other means, QML will not load the plugin.
+ \li \c <Name> is the plugin library name. This is usually not the
+ same as the file name of the plugin binary, which is platform
+ dependent. For example, the library \c MyAppTypes would produce
+ \c libMyAppTypes.so on Linux and \c MyAppTypes.dll on Windows.
+ \li \c <Path> (optional) specifies either:
+ \list
+ \li an absolute path to the directory containing the plugin
+ file, or
+ \li a relative path from the directory containing the \c qmldir
+ file to the directory containing the plugin file.
+ \endlist
+\endlist
+
+By default, the engine searches for the plugin library in the
+directory that contains the \c qmldir file. (The plugin search
+path can be queried with QQmlEngine::pluginPathList() and
+modified using QQmlEngine::addPluginPath().)
+
+Zero or more C++ plugin declarations may exist in the \c qmldir
+file. However, since plugin loading is a relatively expensive
+operation, clients are advised to specify at most a single plugin.
+
+Example:
+\code
+ plugin MyPluginLibrary
+\endcode
+
+\section2 Plugin Classname Declaration
+
+\code
+ classname <C++ plugin class>
+\endcode
+
+Provides the class name of the C++ plugin used by the module.
+
+This information is required for all the QML modules that depend
+on a C++ plugin for additional functionality. Qt Quick applications
+built with static linking cannot resolve the module imports without
+this information.
+
+\section2 Type Description File Declaration
+
+\code
+ typeinfo <File>
+\endcode
+
+Declares a \l{Type Description Files}{type description file} for
+the module that can be read by QML tools such as Qt Creator to
+access information about the types defined by the module's plugins.
+\c <File> is the (relative) file name of a \c .qmltypes file.
+
+Example:
+\code
+ typeinfo mymodule.qmltypes
+\endcode
+
+Without such a file, QML tools may be unable to offer features such
+as code completion for the types defined in your plugins.
+
+\section2 Module Dependencies Declaration
+
+\code
+ depends <ModuleIdentifier> <InitialVersion>
+\endcode
+
+Declares that this module depends on another.
+
+Example:
+\code
+ depends MyOtherModule 1.0
+\endcode
+
+This declaration is necessary only in cases when the dependency is
+hidden: for example, when the C++ code for one module is used to
+load QML (perhaps conditionally), which then depends on other
+modules. In such cases, the \c depends declaration is necessary
+to include the other modules in application packages.
+
+\section2 Module Import Declaration
+
+\code
+ import <ModuleIdentifier> [<Version>]
+\endcode
+
+Declares that this module imports another.
+
+Example:
+\code
+ import MyOtherModule 1.0
+\endcode
+
+The types from the other module are made available in the same type
+namespace as this module is imported into. Omitting the version
+imports the latest version available of the other module. Specifying
+\c auto as version imports the same version as the version of this
+module specified in the QML \c import statement.
+
+\section2 Designer Support Declaration
+
+\code
+ designersupported
+\endcode
+
+Set this property if the plugin is supported by Qt Quick Designer.
+By default, the plugin will not be supported.
+
+A plugin that is supported by Qt Quick Designer has to be properly
+tested. This means that the plugin does not crash when running inside
+the qml2puppet that is used by Qt Quick Designer to execute QML.
+Generally, the plugin should work well in the Qt Quick Designer
+and not cause any show stoppers, like taking excessive amounts of memory,
+slowing down the qml2puppet heavily, or anything else that renders
+the plugin effectively unusable in the Qt Quick Designer.
+
+The items of an unsupported plugin are not painted in the Qt Quick Designer,
+but they are still available as empty boxes and the properties can be edited.
+
+\section2 Preferred Path Declaration
+
+\code
+ prefer <Path>
+\endcode
+
+This property directs the QML engine to load any further files for this
+module from <path>, rather than the current directory. This can be used
+to load files compiled with qmlcachegen.
+
+For example, you can add a module's QML files as resources to a resource
+path \c{:/my/path/MyModule/}. Then, add \c{prefer :/my/path/MyModule} to
+the qmldir file in order to use the files in the resource system, rather
+than the ones in the file system. If you then use qmlcachegen for those,
+the pre-compiled files will be available to any clients of the module.
\section1 Versioning Semantics
@@ -434,7 +443,7 @@ documentation on this, no further action is needed. qmltyperegistrar will
automatically generate the \c .qmltypes files.
Example:
-If your module is in \c /tmp/imports/My/Module, a file caled \c plugins.qmltypes
+If your module is in \c /tmp/imports/My/Module, a file called \c plugins.qmltypes
should be generated alongside the actual plugin binary.
Add the line
diff --git a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
index b1fce12dc3..834977994f 100644
--- a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
+++ b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
@@ -26,6 +26,14 @@ plugins. Library plugins should limit themselves to registering types, as
any manipulation of the engine's root context may cause conflicts or other
issues in the library user's code.
+\note When using the CMake \l qt_add_qml_module API, a plugin will be generated
+automatically for you. It will take care of type registration.
+You only need to write a custom plugin if you have special
+requirements, such as registering custom image
+providers. In that case, pass
+\l{NO_GENERATE_PLUGIN_SOURCE} to the \c qt_add_qml_module
+call to disable the generation of the default plugin.
+
The linker might erroneously remove the generated type registration
function as an optimization. You can prevent that by declaring a synthetic
volatile pointer to the function somewhere in your code. If your module is
diff --git a/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc b/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
index 7ec8a4ff34..0623dd9934 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
@@ -109,6 +109,11 @@ a file system path.
A directory of QML files can also be imported from a remote location if the
directory contains a directory listing \c qmldir file.
+\note This also holds for the implicit import of the directory a QML document
+resides in. If your QML documents are loaded from a remote location, you need
+to add qmldir files even if they don't contain any explicit directory import
+statements. Otherwise your QML documents won't see each other.
+
For example, if the \c myapp directory in the previous example was hosted at
"http://www.my-example-server.com", and the \c mycomponents directory
contained a \c qmldir file defined as follows:
diff --git a/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc b/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
index ee104af29a..c17ced3601 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -305,8 +305,8 @@ default locations to be searched by the engine. By default, this list contains:
Additional import paths can be added through QQmlEngine::addImportPath() or the
\c QML_IMPORT_PATH environment variable. When running the
-\l{Prototyping with qmlscene}{qmlscene} tool, you can also use the \c -I option
-to add an import path.
+\l {Prototyping with the QML Runtime Tool}{qml tool}, you can also use the
+\c -I option to add an import path.
You can specify multiple import paths in the \c QML_IMPORT_PATH environment
variable by joining them using the path separator. On Windows the path separator
diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
index 787e7b920b..8b3ff59ecc 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
@@ -56,6 +56,7 @@ The set of QML object-type attribute types is as follows:
These attributes are discussed in detail below.
\section2 The \e id Attribute
+\keyword QML.id
Every QML object type has exactly one \e id attribute. This attribute is
provided by the language itself, and cannot be redefined or overridden by any
@@ -686,8 +687,8 @@ the role names of the view's model, then those properties will be initialized
with the model's corresponding values.
For more information, visit the \l{Models and Views in Qt Quick} page.
-\sa {QQmlComponent::createWithInitialProperties}, {QQmlApplicationEngine::setInitialProperties}
-and {QQuickView::setInitialProperties} for ways to initialize required properties from C++.
+See \l{QQmlComponent::createWithInitialProperties}, \l{QQmlApplicationEngine::setInitialProperties}
+and \l{QQuickView::setInitialProperties} for ways to initialize required properties from C++.
\section3 Read-Only Properties
@@ -730,6 +731,8 @@ with a particular property is as follows:
}
\endcode
+This is commonly referred to as "on" syntax.
+
It is important to note that the above syntax is in fact an
\l{qtqml-syntax-basics.html#object-declarations}{object declaration} which
will instantiate an object which acts on a pre-existing property.
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc
index 5716f6c386..6e94af6e41 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc
@@ -267,38 +267,18 @@ property is only invoked when the property is reassigned to a different object v
var urlObject = new URL(url);
\endqml
- \section1 Using the url Type
-
- When a relative URL is written to a \c url type property, it is converted
- into a URL object, so \b {matching the URL value against the input string
- value will fail}. Instead, convert the string to a URL using Qt.resolvedUrl()
- for means of comparison, and use \c toString() to get the contents of the URL:
-
- \qml
- Image {
- source: "pics/logo.png"
-
- Component.onCompleted: {
- // This prints 'false'. Although "pics/logo.png" was the input string,
- // it's been converted from a string to a URL, so these two are not the same.
- console.log(source == "pics/logo.png")
-
- // This prints 'true' as Qt.resovledUrl() converts the string into a
- // URL with the correctly resolved path
- console.log(source == Qt.resolvedUrl("pics/logo.png"))
-
- // This prints the absolute path, e.g. "file:///path/to/pics/logo.png"
- console.log(source.toString())
- }
- }
- \endqml
+ \note In Qt 5, URLs were automatically resolved based on the current context
+ when assigning them to any \c url property. This made it impossible to
+ work with relative URLs and it created inconsistent behavior when reading
+ back a URL previously written to a property. Therefore, the behavior was
+ changed in Qt 6.
\note When referring to files stored with the \l{resources.html}{Qt Resource System}
from within QML, you should use "qrc:///" instead of ":/" as QML requires URL paths.
Relative URLs resolved from within that file will use the same protocol.
Additionally, URLs may contain encoded characters using the 'percent-encoding' scheme
- specified by \l {http://tools.ietf.org/html/rfc3986}{RFC 3986}. These characters
+ specified by \l {https://datatracker.ietf.org/doc/html/rfc3986}{RFC 3986}. These characters
will be preserved within properties of type \c url, to allow QML code to
construct precise URL values.
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc
index 83544b1a66..a3df68b29b 100644
--- a/src/qml/doc/src/qmltypereference.qdoc
+++ b/src/qml/doc/src/qmltypereference.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\qmlmodule QtQml 2.\QtMinorVersion
+\qmlmodule QtQml
\title Qt QML QML Types
\ingroup qmlmodules
\brief List of QML types provided by the Qt QML module
diff --git a/src/qml/doc/src/qt6-changes.qdoc b/src/qml/doc/src/qt6-changes.qdoc
index 70c61849e7..9798718b96 100644
--- a/src/qml/doc/src/qt6-changes.qdoc
+++ b/src/qml/doc/src/qt6-changes.qdoc
@@ -213,8 +213,8 @@
engine.installTranslatorFunctions();
\newcode
QJSEngine engine;
- engine.installExtensions(QJSEngine::TranslationExtension
- \endcode;
+ engine.installExtensions(QJSEngine::TranslationExtension);
+ \endcode
\endlist
diff --git a/src/qml/doc/src/qtqml-writing-a-module.qdoc b/src/qml/doc/src/qtqml-writing-a-module.qdoc
new file mode 100644
index 0000000000..6ad820552e
--- /dev/null
+++ b/src/qml/doc/src/qtqml-writing-a-module.qdoc
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page qtqml-writing-a-module.html
+\title Writing QML Modules
+\brief How to write a custom QML module.
+
+You can declare a QML module using the \l{qt_add_qml_module}
+{CMake QML Module API} to:
+
+\list
+\li Generate \l {Module Definition qmldir Files}{qmldir} and
+ \l {Type Description Files}{*.qmltypes files}.
+\li Register C++ types annotated with \l QML_ELEMENT.
+\li Invoke \l {Ahead-of-Time-Compilation}{qmlcachegen}.
+\li Provide modules both in the physical and in \l{The Qt Resource System}
+ {resource file system}.
+\li Use the pre-compiled versions of QML files.
+\li Bundle the module's files in the resource file system.
+\li Combine QML files and C++-based types in the same module.
+\li Create a backing library and an optional plugin. Link the backing library
+ into the application to avoid loading the plugin at run time.
+\endlist
+
+All the above actions can also be configured separately.
+For more information, see \l {qt_add_qml_module}{CMake QML Module API}.
+
+\section1 Multiple QML Modules in One Binary
+
+You can add multiple QML modules into the same binary. Define a CMake target for
+each module and then link the targets to the executable.
+If the extra targets are all static libraries, the result will be one binary,
+which contains multiple QML modules. In short you can create an application
+like this:
+
+\badcode
+myProject
+ | - CMakeLists.txt
+ | - main.cpp
+ | - main.qml
+ | - onething.h
+ | - onething.cpp
+ | - ExtraModule
+ | - CMakeLists.txt
+ | - Extra.qml
+ | - extrathing.h
+ | - extrathing.cpp
+\endcode
+
+To begin, let's assume main.qml contains an instantiation of Extra.qml:
+
+ \badcode
+ import ExtraModule
+ Extra { ... }
+ \endcode
+
+The extra module has to be a static library so that you can link it
+into the main program. Therefore, state as much in ExtraModule/CMakeLists.txt:
+
+\quotefromfile qml/CMakeLists.txt
+\printuntil extrathing.h
+\printuntil )
+
+This generates two targets: \c extra_module for the backing library, and
+\c extra_moduleplugin for the plugin. Being a static library too, the plugin cannot
+be loaded at runtime.
+
+In myProject/CMakeLists.txt you need to specify the QML module that main.qml
+and any types declared in onething.h are part of:
+
+\quotefromfile qml/myProject-CMakeLists.txt
+\printuntil onething.h
+\printuntil )
+
+
+From there, you add the subdirectory for the extra module:
+
+\quotefromfile qml/CMakeLists.txt
+\skipto add_subdirectory
+\printuntil )
+
+To ensure that linking the extra module works correctly, you need to:
+
+\list
+\li Define a symbol in the extra module.
+\li Create a reference to the symbol from the main program.
+\endlist
+
+QML plugins contain a symbol you can use for this purpose.
+You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbol.
+Add the following code to the main.cpp:
+
+\badcode
+#include <QtQml/qqmlextensionplugin.h>
+Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
+\endcode
+
+\c ExtraModulePlugin is the name of the generated plugin class. It's composed
+of the module URI with \c Plugin appended to it. Then, in the main program's
+CMakeLists.txt, link the plugin, not the backing library, into the main program:
+
+\quotefromfile qml/myProject-CMakeLists.txt
+\skipto target_link_libraries
+\printuntil )
+
+\section1 Exporting Multiple Major Versions from The Same Module
+
+\l qt_add_qml_module by default considers the major version given in its
+URI argument, even if the individual types declare other versions in their
+added specific version via \l QT_QML_SOURCE_VERSIONS or \l Q_REVISION.
+If a module is available under more than one version, you also need to decide
+what versions the individual QML files are available under. To declare further
+major versions, you can use the \c PAST_MAJOR_VERSIONS option to
+\c qt_add_qml_module as well as the \c {QT_QML_SOURCE_VERSIONS} property on
+individual QML files.
+
+\quotefile qml/MajorProject-CMakeLists.txt
+
+\c MyModule is available in major versions 1, 2, and 3. The maximum version
+available is 3.2. You can import any version 1.x or 2.x with a positive x. For
+Thing.qml and OtherThing.qml we have added explicit version information.
+Thing.qml is available from version 1.4, and OtherThing.qml is available from
+version 2.2. You have to specify the later versions, too, in each
+\c set_source_files_properties() because you may remove QML files
+from a module when bumping the major version. There is no explicit version
+information for OneMoreThing.qml. This means that OneMoreThing.qml is available
+in all major versions, from minor version 0.
+
+With this setup, the generated registration code will register the module
+\c versionsqmlRegisterModule() for each of the major versions. This way, all
+versions can be imported.
+
+
+\section1 Custom Directory Layouts
+
+The easiest way to structure QML modules is to keep them in directories named by
+their URIs. For example, a module My.Extra.Module would live in a directory
+My/Extra/Module relative to the application that uses it. This way, they can
+easily be found at run time and by any tools.
+
+In more complex projects, this convention can be too limiting. You might for
+instance want to group all QML modules in one place to avoid polluting the
+project's root directory. Or you want to reuse a single module in multiple
+applications. For those cases, \c QT_QML_OUTPUT_DIRECTORY in combination with
+\c RESOURCE_PREFIX and \l IMPORT_PATH can be used.
+
+To collect QML modules into a specific output directory, for example a
+subdirectory "qml" in the build directory \l QT_QML_OUTPUT_DIRECTORY, set the
+following in the top-level CMakeLists.txt:
+
+\badcode
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
+\endcode
+
+The output directories of QML modules move to the new location.
+Likewise, the \c qmllint and \c qmlcachegen invocations are automatically
+adapted to use the new output directory as an \l {QML Import Path}{import path}.
+Because the new output directory is not part of the default QML import path,
+you have to add it explicitly at run time, so that the QML modules can be found.
+
+
+Now that the physical file system is taken care of, you may still want to move
+the QML modules into a different place in the resource file system. This is what
+the RESOURCE_PREFIX option is for. You have to specify it separately in
+each \l qt_add_qml_module. The QML module will then be placed under the specified
+prefix, with a target path generated from the URI appended. For example,
+consider the following module:
+
+\code
+qt_add_qml_module(
+ URI My.Great.Module
+ VERSION 1.0
+ RESOURCE_PREFIX /example.com/qml
+ QML_FILES
+ A.qml
+ B.qml
+)
+\endcode
+
+This will add a directory \c example.com/qml/My/Great/Module to the resource file
+system and place the QML module defined above in it. You don't strictly need to
+add the resource prefix to the QML import path as the module can still be found
+in the physical file system. However, it generally is a good idea to add the
+resource prefix to the QML import path because loading from the resource file
+system is faster than loading from the physical file system for most modules.
+
+If the QML modules are meant to be used in a larger project with multiple import
+paths, you'll have to do an additional step: Even if you add the import paths at
+run time, tooling like \c qmllint does not have access to it, and might fail to
+find the correct dependencies. Use \c IMPORT_PATH to tell tooling about the
+additional paths it has to consider. For example:
+
+\badcode
+qt_add_qml_module(
+ URI My.Dependent.Module
+ VERSION 1.0
+ QML_FILES
+ C.qml
+ IMPORT_PATH "/some/where/else"
+)
+\endcode
+
+\section1 Eliminating Run Time File System Access
+
+If all QML modules are always loaded from the resource
+file system, you can deploy the application as a single binary. Let's first
+consider the simple case:
+
+\badcode
+QQmlEngine qmlEngine;
+qmlEngine.addImportPath(QStringLiteral(":/"));
+// Use qmlEngine to load the main.qml file.
+\endcode
+
+\note ":/" is used for simplicity here. See \l {Custom Directory Layouts}
+for more complex cases.
+
+If all the modules are linked into the application and if you're following
+the default resource directory structure, do not add any further import paths as
+those might override the one you added.
+
+If you have specified a custom \c RESOURCE_PREFIX, you have to add the custom
+resource prefix to the import path instead. You can also add multiple resource
+prefixes.
+
+The path \c :/qt-project.org/imports/ is part of the default QML import path. If
+you use it, you don't have to specially add it. Qt's own QML modules are placed
+there, though. You have to be careful not to overwrite them. For modules that are
+heavily re-used across different projects \c :/qt-project.org/imports/ is
+acceptable. By using it you can avoid forcing all the users to add custom
+import paths.
+
+\section1 Integrating custom QML plugins
+
+If you bundle an \l {QQuickImageProvider}{image provider} in the QML module, you
+need to implement the \l {QQmlEngineExtensionPlugin::initializeEngine()}
+method. This, in turn, makes it necessary to write the own plugin. To support
+this use case, \l NO_GENERATE_PLUGIN_SOURCE can be used.
+
+Let's consider a module that provides its own plugin source:
+
+\quotefile qml/myimageprovider.txt
+
+You may declare an image provider in myimageprovider.h, like this:
+
+\badcode
+class MyImageProvider : public QQuickImageProvider
+{
+ [...]
+};
+\endcode
+
+In plugin.cpp you can then define the QQmlEngineExtensionPlugin:
+
+\quotefile qml/plugin.cpp.txt
+
+This will make the image provider available. The plugin and the backing library
+both are in the same CMake target imageproviderplugin. This is done so that the
+linker does not drop parts of the module in various scenarios.
+
+As the plugin creates an image provider, it no longer has a trivial
+\c initializeEngine function. Therefore, the plugin is no longer optional.
+
+*/
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index e1ff693195..9f26c9328b 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -132,8 +132,14 @@ The QML framework allows QML code to contain JavaScript expressions and for
the QML code to interact with C++ code.
\list
-\li \l{Important C++ Classes Provided By The Qt QML Module}
-\li \l{Integrating QML and C++}
+ \li \l {Overview - QML and C++ Integration}
+ \li \l {Data Type Conversion Between QML and C++}
+ \li \l {Integrating with JavaScript values from C++}
+ \li \l {Exposing Attributes of C++ Types to QML}
+ \li \l {Defining QML Types from C++}
+ \li \l {Writing QML Modules}
+ \li \l {Important C++ Classes Provided By The Qt QML Module}
+ \li \l {Integrating QML and C++}
\endlist
\omit
diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h
index ae436d53b0..56794779a6 100644
--- a/src/qml/inlinecomponentutils_p.h
+++ b/src/qml/inlinecomponentutils_p.h
@@ -55,24 +55,38 @@
namespace icutils {
struct Node {
+private:
+ using IndexType = std::vector<QV4::CompiledData::InlineComponent>::size_type;
+ using IndexField = quint32_le_bitfield_member<0, 30, IndexType>;
+ using TemporaryMarkField = quint32_le_bitfield_member<30, 1>;
+ using PermanentMarkField = quint32_le_bitfield_member<31, 1>;
+ quint32_le_bitfield_union<IndexField, TemporaryMarkField, PermanentMarkField> m_data;
+
+public:
Node() = default;
Node(const Node &) = default;
Node(Node &&) = default;
Node& operator=(Node const &) = default;
Node& operator=(Node &&) = default;
- bool operator==(Node const &other) const {return index == other.index;}
+ bool operator==(Node const &other) const {return m_data.data() == other.m_data.data(); }
+
+ Node(IndexType s) : m_data(QSpecialIntegerBitfieldZero) { m_data.set<IndexField>(s); }
+
+ bool hasPermanentMark() const { return m_data.get<PermanentMarkField>(); }
+ bool hasTemporaryMark() const { return m_data.get<TemporaryMarkField>(); }
+
+ void setPermanentMark()
+ {
+ m_data.set<TemporaryMarkField>(0);
+ m_data.set<PermanentMarkField>(1);
+ }
- Node(std::vector<QV4::CompiledData::InlineComponent>::size_type s) {
- index = quint32(s);
- temporaryMark = 0;
- permanentMark = 0;
+ void setTemporaryMark()
+ {
+ m_data.set<TemporaryMarkField>(1);
}
- union {
- quint32_le_bitfield<0, 30> index;
- quint32_le_bitfield<30, 1> temporaryMark;
- quint32_le_bitfield<31, 1> permanentMark;
- };
+ IndexType index() const { return m_data.get<IndexField>(); }
};
using AdjacencyList = std::vector<std::vector<Node*>>;
@@ -109,8 +123,11 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
auto referencedInICObjectIndex = ic.objectIndex + 1;
while (int(referencedInICObjectIndex) < objectContainer->objectCount()) {
auto potentiallyReferencedInICObject = objectContainer->objectAt(referencedInICObjectIndex);
- bool stillInIC = !(potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- && (potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::InPartOfInlineComponent);
+ bool stillInIC
+ = !potentiallyReferencedInICObject->hasFlag(
+ QV4::CompiledData::Object::IsInlineComponentRoot)
+ && potentiallyReferencedInICObject->hasFlag(
+ QV4::CompiledData::Object::InPartOfInlineComponent);
if (!stillInIC)
break;
createEdgeFromTypeRef(objectContainer->resolvedType(potentiallyReferencedInICObject->inheritedTypeNameIndex));
@@ -120,21 +137,20 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
};
inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle, std::vector<Node> &nodesSorted) {
- if (node->permanentMark)
+ if (node->hasPermanentMark())
return;
- if (node->temporaryMark) {
+ if (node->hasTemporaryMark()) {
hasCycle = true;
return;
}
- node->temporaryMark = 1;
+ node->setTemporaryMark();
- auto const &edges = adjacencyList[node->index];
+ auto const &edges = adjacencyList[node->index()];
for (auto edgeTarget =edges.begin(); edgeTarget != edges.end(); ++edgeTarget) {
topoVisit(*edgeTarget, adjacencyList, hasCycle, nodesSorted);
}
- node->temporaryMark = 0;
- node->permanentMark = 1;
+ node->setPermanentMark();
nodesSorted.push_back(*node);
};
@@ -145,7 +161,7 @@ inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjac
hasCycle = false;
auto currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) {
- return node.permanentMark == 0;
+ return !node.hasPermanentMark();
});
// Do a topological sort of all inline components
// afterwards, nodesSorted contains the nodes for the inline components in reverse topological order
@@ -153,7 +169,7 @@ inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjac
Node& currentNode = *currentNodeIt;
topoVisit(&currentNode, adjacencyList, hasCycle, nodesSorted);
currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) {
- return node.permanentMark == 0;
+ return !node.hasPermanentMark();
});
}
return nodesSorted;
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index b21acf93c7..ced4c3e057 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -554,6 +554,8 @@ void BaselineJIT::generate_ThrowException()
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore);
as->gotoCatchException();
+
+ // LOAD_ACC(); <- not needed here since it would be unreachable.
}
void BaselineJIT::generate_GetException() { as->getException(); }
@@ -561,9 +563,11 @@ void BaselineJIT::generate_SetException() { as->setException(); }
void BaselineJIT::generate_CreateCallContext()
{
+ STORE_ACC();
as->prepareCallWithArgCount(1);
as->passCppFrameAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore);
+ LOAD_ACC();
}
void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index a61e2829eb..2c6c579e4f 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -1105,13 +1105,9 @@ QJSValue QJSEngine::catchError()
after installing translators in your application. By convention, an empty string
means no translation from the language used in the source code is intended to occur.
*/
-void QJSEngine::setUiLanguage(const QString &language)
-{
+void QJSEngine::setUiLanguage(const QString &language) {
Q_D(QJSEngine);
- if (language == d->uiLanguage)
- return;
- d->uiLanguage = language;
- emit uiLanguageChanged();
+ d->uiLanguage = language; // property takes care of signal emission if necessary
}
QString QJSEngine::uiLanguage() const
diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h
index fb88781852..aa846bb246 100644
--- a/src/qml/jsapi/qjsengine_p.h
+++ b/src/qml/jsapi/qjsengine_p.h
@@ -107,8 +107,8 @@ public:
// Shared by QQmlEngine
mutable QRecursiveMutex mutex;
-
- QProperty<QString> uiLanguage;
+ void uiLanguageChanged() { Q_Q(QJSEngine); if (q) q->uiLanguageChanged(); }
+ Q_OBJECT_BINDABLE_PROPERTY(QJSEnginePrivate, QString, uiLanguage, &QJSEnginePrivate::uiLanguageChanged);
// These methods may be called from the QML loader thread
inline QQmlPropertyCache *cache(QObject *obj, QTypeRevision version = QTypeRevision(), bool doRef = false);
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
index f248e12398..ce43b958cf 100644
--- a/src/qml/jsapi/qjsmanagedvalue.cpp
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -909,7 +909,7 @@ QJSValue QJSManagedValue::property(quint32 arrayIndex) const
if (QV4::String *string = d->as<QV4::String>()) {
const QString qString = string->toQString();
- if (arrayIndex < qString.size())
+ if (arrayIndex < quint32(qString.size()))
return qString.sliced(arrayIndex, 1);
return QJSValue();
}
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 03cd1e9aa9..832eaca8da 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -142,6 +142,16 @@
integers.append(jsArray.property(i).toInt());
}
\endcode
+
+ \section2 Converting to JSON
+
+ It's possible to convert a QJSValue to a JSON type. For example,
+ to convert to an array, use \l QJSEngine::fromScriptValue():
+
+ \code
+ const QJsonValue jsonValue = engine.fromScriptValue<QJsonValue>(jsValue);
+ const QJsonArray jsonArray = jsonValue.toArray();
+ \endcode
*/
/*!
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index cea48fd9e2..69837d6575 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -75,7 +75,7 @@ enum PointerMask: quintptr {
IsString = 0x1
};
-class Q_AUTOTEST_EXPORT QJSValuePrivate
+class QJSValuePrivate
{
static const QV4::Value *managedValue(const QV4::Value &v)
{
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 1873c41261..4000deaeab 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -72,7 +72,7 @@ struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object {
void init(size_t length);
void init(const QByteArray& array);
void destroy();
- std::aligned_storage_t<sizeof(QArrayDataPointer<char>), alignof(QArrayDataPointer<char>)> d;
+ struct { alignas(QArrayDataPointer<char>) unsigned char data[sizeof(QArrayDataPointer<char>)]; } d;
const QArrayDataPointer<char> &data() const { return *reinterpret_cast<const QArrayDataPointer<char> *>(&d); }
QArrayDataPointer<char> &data() { return *reinterpret_cast<QArrayDataPointer<char> *>(&d); }
bool isShared;
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 577cc11cf4..20501f9d03 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -563,7 +563,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n)
ScopedValue v(scope);
for (uint i = 0; i < n; ++i)
obj->arraySet(oldSize + i, (v = otherObj->get(i)));
- } else if (other && other->isSparse()) {
+ } else if (other->isSparse()) {
Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d());
if (other->hasAttributes()) {
ScopedValue v(scope);
@@ -586,7 +586,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n)
obj->arrayPut(oldSize, os->values.data() + os->offset, chunk);
toCopy -= chunk;
if (toCopy)
- obj->setArrayLength(oldSize + chunk + toCopy);
+ obj->arrayPut(oldSize + chunk, os->values.data(), toCopy);
}
return oldSize + n;
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
index 199b1a728a..51387edf6e 100644
--- a/src/qml/jsruntime/qv4arrayiterator.cpp
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -86,18 +86,18 @@ ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const
return IteratorPrototype::createIterResultObject(scope.engine, Value::fromInt32(index), false);
}
- ReturnedValue elementValue = a->get(index);
+ QV4::ScopedValue elementValue(scope, a->get(index));
CHECK_EXCEPTION();
if (itemKind == ValueIteratorKind) {
- return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false);
+ return IteratorPrototype::createIterResultObject(scope.engine, elementValue, false);
} else {
Q_ASSERT(itemKind == KeyValueIteratorKind);
ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
resultArray->arrayReserve(2);
resultArray->arrayPut(0, Value::fromInt32(index));
- resultArray->arrayPut(1, Value::fromReturnedValue(elementValue));
+ resultArray->arrayPut(1, elementValue);
resultArray->setArrayLengthUnchecked(2);
return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index f3e2b445d1..6cff8d3c9b 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -65,6 +65,11 @@ public:
s_staticUnits.insert(file, staticUnit);
}
+ void remove(const QString &file)
+ {
+ s_staticUnits.remove(file);
+ }
+
private:
QMutexLocker<QMutex> m_lock;
@@ -105,4 +110,10 @@ CompiledData::Unit *CompilationUnitMapper::get(
return data;
}
+void CompilationUnitMapper::invalidate(const QString &cacheFilePath)
+{
+ StaticUnitCache cache;
+ cache.remove(cacheFilePath);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 10ad0a6ede..c6773fdd0e 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -69,6 +69,7 @@ public:
CompiledData::Unit *get(
const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ static void invalidate(const QString &cacheFilePath);
private:
CompiledData::Unit *open(
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 9ab0bb5628..12225d3866 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -78,10 +78,21 @@
QTBUG-75585 for an explanation and possible workarounds.
*/
#define USE_QTZ_SYSTEM_ZONE
+#elif defined(Q_OS_WASM)
+/*
+ TODO: evaluate using this version of the code more generally, rather than
+ the #else branches of the various USE_QTZ_SYSTEM_ZONE choices. It might even
+ work better than the timezone variant; experiments needed.
+*/
+// Kludge around the lack of time-zone info using QDateTime.
+// It uses localtime() and friends to determine offsets from UTC.
+#define USE_QDT_LOCAL_TIME
#endif
#ifdef USE_QTZ_SYSTEM_ZONE
#include <QtCore/QTimeZone>
+#elif defined(USE_QDT_LOCAL_TIME)
+// QDateTime already included above
#else
# ifdef Q_OS_WIN
# include <windows.h>
@@ -356,6 +367,7 @@ static inline double MakeDate(double day, double time)
mean a whole day of DST offset for some zones, that have crossed the
international date line. This shall confuse client code.) The bug report
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
+ and they've now changed the spec so that the following conforms to it ;^>
*/
static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
@@ -363,6 +375,12 @@ static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC t
return QTimeZone::systemTimeZone().offsetFromUtc(
QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
+#elif defined(USE_QDT_LOCAL_TIME)
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
+{
+ return QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC
+ ).toLocalTime().offsetFromUtc() * 1e3 - localTZA;
+}
#else
// This implementation fails to take account of past changes in standard offset.
static inline double DaylightSavingTA(double t, double /*localTZA*/)
@@ -632,7 +650,9 @@ static inline double ParseString(const QString &s, double localTZA)
QStringLiteral("d MMMM, yyyy hh:mm"),
QStringLiteral("d MMMM, yyyy hh:mm:ss"),
+ // ISO 8601 and RFC 2822 with a GMT as prefix on its offset, or GMT as zone.
QStringLiteral("yyyy-MM-dd hh:mm:ss t"),
+ QStringLiteral("ddd, d MMM yyyy hh:mm:ss t"),
};
for (const QString &format : formats) {
@@ -722,6 +742,26 @@ static double getLocalTZA()
// TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above.
// Standard offset, with no daylight-savings adjustment, in ms:
return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3;
+#elif defined(USE_QDT_LOCAL_TIME)
+ QDate today = QDate::currentDate();
+ QDateTime near = today.startOfDay(Qt::LocalTime);
+ // Early out if we're in standard time anyway:
+ if (!near.isDaylightTime())
+ return near.offsetFromUtc() * 1000;
+ int year, month;
+ today.getDate(&year, &month, nullptr);
+ // One of the solstices is probably in standard time:
+ QDate summer(year, 6, 21), winter(year - (month < 7 ? 1 : 0), 12, 21);
+ // But check the one closest to the present by preference, in case there's a
+ // standard time offset change between them:
+ QDateTime far = summer.startOfDay(Qt::LocalTime);
+ near = winter.startOfDay(Qt::LocalTime);
+ if (month > 3 && month < 10)
+ near.swap(far);
+ bool isDst = near.isDaylightTime();
+ if (isDst && far.isDaylightTime()) // Permanent DST, probably an hour west:
+ return (qMin(near.offsetFromUtc(), far.offsetFromUtc()) - 3600) * 1000;
+ return (isDst ? far : near).offsetFromUtc() * 1000;
#else
# ifdef Q_OS_WIN
TIME_ZONE_INFORMATION tzInfo;
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 6883c219bc..c047298aba 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -356,6 +356,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
if (ok && envMaxGCStackSize > 0)
m_maxGCStackSize = envMaxGCStackSize;
+ // We allocate guard pages around our stacks.
+ const size_t guardPages = 2 * WTF::pageSize();
+
memoryManager = new QV4::MemoryManager(this);
if (maxCallDepth == -1) {
@@ -366,7 +369,13 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
if (!ok || maxCallDepth <= 0) {
#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
+#ifdef Q_OS_QNX
+ maxCallDepth = 640; // QNX's stack is only 512k by default
+#elif defined(Q_OS_WIN)
+ maxCallDepth = 640; // We've seen crashes around 750.
+#else
maxCallDepth = 1234;
+#endif
#else
// no (tail call) optimization is done, so there'll be a lot mare stack frames active
maxCallDepth = 200;
@@ -379,9 +388,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
- *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ m_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
jsStackBase = (Value *)jsStack->base();
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
@@ -389,9 +398,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *gcStack = WTF::PageAllocation::allocate(
+ m_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
{
ok = false;
@@ -1521,6 +1530,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
if (typeHint == QMetaType::Bool)
return QVariant(value.toBoolean());
+ if (typeHint == QMetaType::Double)
+ return QVariant(value.toNumber());
+
+ if (typeHint == QMetaType::Float)
+ return QVariant(float(value.toNumber()));
+
if (typeHint == QMetaType::QJsonValue)
return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
@@ -1996,6 +2011,25 @@ int ExecutionEngine::maxGCStackSize() const
return m_maxGCStackSize;
}
+/*!
+ \internal
+ Returns \a length converted to int if its safe to
+ pass to \c Scope::alloc.
+ Otherwise it throws a RangeError, and returns 0.
+ */
+int ExecutionEngine::safeForAllocLength(qint64 len64)
+{
+ if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) {
+ this->throwRangeError(QStringLiteral("Invalid array length."));
+ return 0;
+ }
+ if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) {
+ this->throwRangeError(QStringLiteral("Array too large for apply()."));
+ return 0;
+ }
+ return len64;
+}
+
ReturnedValue ExecutionEngine::global()
{
return globalObject->asReturnedValue();
@@ -2188,7 +2222,7 @@ void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
{
- if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen)
+ if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen())
return;
QV4::Scope scope(v4);
@@ -2297,6 +2331,8 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
case QMetaType::QByteArray:
if (const ArrayBuffer *ab = value.as<ArrayBuffer>())
*reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
+ else if (const String *string = value.as<String>())
+ *reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8();
else
*reinterpret_cast<QByteArray*>(data) = QByteArray();
return true;
@@ -2462,25 +2498,6 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
}
}
-#if 0
- if (isQtVariant(value)) {
- const QVariant &var = variantValue(value);
- // ### Enable once constructInPlace() is in qt master.
- if (var.userType() == type) {
- QMetaType::constructInPlace(type, data, var.constData());
- return true;
- }
- if (var.canConvert(type)) {
- QVariant vv = var;
- vv.convert(type);
- Q_ASSERT(vv.userType() == type);
- QMetaType::constructInPlace(type, data, vv.constData());
- return true;
- }
-
- }
-#endif
-
// Try to use magic; for compatibility with qjsvalue_cast.
if (convertToNativeQObject(value, metaType, reinterpret_cast<void **>(data)))
@@ -2531,6 +2548,15 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
} else if (metaType == QMetaType::fromType<QJSValue>()) {
QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue());
return true;
+ } else if (!isPointer) {
+ QVariant val;
+ if (QQml_valueTypeProvider()->createValueType(
+ metaType.id(), QJSValuePrivate::fromReturnedValue(value.asReturnedValue()), val)) {
+ Q_ASSERT(val.metaType() == metaType);
+ metaType.destruct(data);
+ metaType.construct(data, val.constData());
+ return true;
+ }
}
return false;
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index db6bfcc84b..0d3b279377 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -694,6 +694,7 @@ public:
int maxGCStackSize() const;
bool checkStackLimits();
+ int safeForAllocLength(qint64 len64);
bool canJIT(Function *f = nullptr)
{
@@ -778,6 +779,9 @@ public:
int argc, void **args, QMetaType *types);
private:
+ template<int Frames>
+ friend struct ExecutionEngineCallDepthRecorder;
+
QV4::ReturnedValue fromData(
const QMetaType &type, const void *ptr, const QVariant *variant = nullptr);
@@ -813,12 +817,15 @@ private:
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
+template<int Frames = 1>
struct ExecutionEngineCallDepthRecorder
{
ExecutionEngine *ee;
- ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; }
- ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; }
+ ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ee->callDepth += Frames; }
+ ~ExecutionEngineCallDepthRecorder() { ee->callDepth -= Frames; }
+
+ bool hasOverflow() const { return ee->callDepth >= ExecutionEngine::maxCallDepth; }
};
inline bool ExecutionEngine::checkStackLimits()
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index ade5204f9e..7fe3862172 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -69,14 +69,16 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/QScopedValueRollback>
-#if defined(QML_COMPILE_HASH)
+static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
+
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
# ifdef Q_OS_LINUX
// Place on a separate section on Linux so it's easier to check from outside
// what the hash version is.
__attribute__((section(".qml_compile_hash")))
# endif
-const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH;
-static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1,
+const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
"Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
#else
# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
@@ -159,10 +161,10 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- uint f = re->flags;
+ uint f = re->flags();
const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
runtimeRegularExpressions[i] = QV4::RegExp::create(
- engine, stringAt(re->stringIndex), flags);
+ engine, stringAt(re->stringIndex()), flags);
}
if (data->lookupTableSize) {
@@ -173,7 +175,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
QV4::Lookup *l = runtimeLookups + i;
CompiledData::Lookup::Type type
- = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags));
+ = CompiledData::Lookup::Type(uint(compiledLookups[i].typeAndFlags()));
if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
else if (type == CompiledData::Lookup::Type_Setter)
@@ -182,7 +184,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
- l->nameIndex = compiledLookups[i].nameIndex;
+ l->nameIndex = compiledLookups[i].nameIndex();
}
}
@@ -203,8 +205,8 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
runtimeClasses[i]
= runtimeClasses[i]->addMember(
engine->identifierTable->asPropertyKey(
- runtimeStrings[member->nameOffset]),
- member->isAccessor
+ runtimeStrings[member->nameOffset()]),
+ member->isAccessor()
? QV4::Attr_Accessor
: QV4::Attr_Data);
}
@@ -316,20 +318,8 @@ void ExecutableCompilationUnit::unlink()
propertyCaches.clear();
if (runtimeLookups) {
- for (uint i = 0; i < data->lookupTableSize; ++i) {
- QV4::Lookup &l = runtimeLookups[i];
- if (l.getter == QV4::QObjectWrapper::lookupGetter
- || l.setter == QV4::QObjectWrapper::lookupSetter
- || l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
- if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
- pc->release();
- }
- if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
- || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
- if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
- pc->release();
- }
- }
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].releasePropertyCache();
}
dependentScripts.clear();
@@ -400,7 +390,7 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com
const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId());
}
Q_ASSERT(!namedObjectCache.isEmpty());
return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
@@ -452,13 +442,14 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi
// We need to first iterate over all inline components, as the containing component might create instances of them
// and in that case we need to add its object count
for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
- const auto &ic = allICs.at(nodeIt->index);
+ const auto &ic = allICs.at(nodeIt->index());
int lastICRoot = ic.objectIndex;
for (int i = ic.objectIndex; i<objectCount(); ++i) {
const QV4::CompiledData::Object *obj = objectAt(i);
bool leftCurrentInlineComponent =
- (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent);
+ (i != lastICRoot
+ && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
+ || !obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent);
if (leftCurrentInlineComponent)
break;
inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings;
@@ -488,9 +479,9 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi
int objectCount = 0;
for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
const QV4::CompiledData::Object *obj = objectAt(i);
- if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) {
+ if (obj->hasFlag(QV4::CompiledData::Object::InPartOfInlineComponent))
continue;
- }
+
bindingCount += obj->nBindings;
if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
const auto type = typeRef->type();
@@ -632,7 +623,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
referenceErrorMessage += QStringLiteral(" because ");
referenceErrorMessage += url.toString(QUrl::RemoveFragment);
referenceErrorMessage += QStringLiteral(" is not an object");
- engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ engine->throwReferenceError(
+ referenceErrorMessage, fileName(),
+ entry.location.line(), entry.location.column());
return nullptr;
}
@@ -651,7 +644,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
if (!valuePtr) {
QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
referenceErrorMessage += importName->toQString();
- engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ engine->throwReferenceError(
+ referenceErrorMessage, fileName(),
+ entry.location.line(), entry.location.column());
return nullptr;
}
imports[i] = valuePtr;
@@ -668,7 +663,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
if (!dependentModuleUnit->resolveExport(importName)) {
QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
referenceErrorMessage += importName->toQString();
- engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ engine->throwReferenceError(
+ referenceErrorMessage, fileName(),
+ entry.location.line(), entry.location.column());
return nullptr;
}
}
@@ -860,8 +857,14 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
[&unitUrl, errorString](const char *data, quint32 size) {
- return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
- size, errorString);
+ const QString cachePath = localCacheFilePath(unitUrl);
+ if (CompiledData::SaveableUnitPointer::writeDataToFile(
+ cachePath, data, size, errorString)) {
+ CompilationUnitMapper::invalidate(cachePath);
+ return true;
+ }
+
+ return false;
});
}
@@ -891,7 +894,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind
{
using namespace CompiledData;
#if QT_CONFIG(translation)
- switch (binding->type) {
+ switch (binding->type()) {
case Binding::Type_TranslationById: {
const TranslationData &translation
= data->translations()[binding->value.translationDataIndex];
@@ -952,9 +955,15 @@ bool ExecutableCompilationUnit::verifyHeader(
}
}
-#if defined(QML_COMPILE_HASH)
- if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) {
- *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match");
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
+ if (qstrncmp(qml_compile_hash, unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
+ *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
+ .arg(QString::fromLatin1(
+ QByteArray(unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()),
+ QString::fromLatin1(
+ QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()));
return false;
}
#else
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 6ab9a87ec0..d92714a9e2 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -208,7 +208,8 @@ QString Function::prettyName(const Function *function, const void *code)
QQmlSourceLocation Function::sourceLocation() const
{
- return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
+ return QQmlSourceLocation(
+ sourceFile(), compiledFunction->location.line(), compiledFunction->location.column());
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 7577161c01..20afeff140 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -138,6 +138,7 @@ public:
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; }
+ inline bool isClosureWrapper() const { return compiledFunction->flags & CompiledData::Function::IsClosureWrapper; }
QQmlSourceLocation sourceLocation() const;
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index d3bafbe055..798cfc131d 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -387,15 +387,10 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
if (!arr)
return v4->throwTypeError();
- const qint64 len64 = arr->getLength();
- if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max()))
- return v4->throwRangeError(QStringLiteral("Invalid array length."));
- if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop))
- return v4->throwRangeError(QStringLiteral("Array too large for apply()."));
-
- const uint len = uint(len64);
-
Scope scope(v4);
+ const uint len = v4->safeForAllocLength(arr->getLength());
+ CHECK_EXCEPTION();
+
Value *arguments = scope.alloc<Scope::Uninitialized>(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
index 6eb7b306e0..3dd2870e5b 100644
--- a/src/qml/jsruntime/qv4generatorobject_p.h
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -87,7 +87,6 @@ struct GeneratorPrototype : FunctionObject {
#define GeneratorObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, context) \
- Member(class, Pointer, GeneratorFunction *, function) \
Member(class, NoMark, GeneratorState, state) \
Member(class, NoMark, JSTypesStackFrame, cppFrame) \
Member(class, Pointer, ArrayObject *, values) \
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 1493456853..0abdd76c96 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -307,7 +307,6 @@ struct PropertyAttributes
void clear() { m_all = 0; }
bool isEmpty() const { return !m_all; }
- uint flags() const { return m_flags; }
uint all() const { return m_all; }
bool operator==(PropertyAttributes other) {
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 4287563c94..eabb220c50 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -178,7 +178,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s)
data->values.size = s;
}
-PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i)
+PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) const
{
Q_ASSERT(data && i < size());
return PropertyKey::fromId(data->values.values[i].rawValue());
@@ -265,6 +265,13 @@ namespace Heap {
void InternalClass::init(ExecutionEngine *engine)
{
+// InternalClass is automatically zeroed during allocation:
+// prototype = nullptr;
+// parent = nullptr;
+// size = 0;
+// numRedundantTransitions = 0;
+// flags = 0;
+
Base::init();
new (&propertyTable) PropertyHash();
new (&nameMap) SharedInternalClassData<PropertyKey>(engine);
@@ -273,13 +280,6 @@ void InternalClass::init(ExecutionEngine *engine)
this->engine = engine;
vtable = QV4::InternalClass::staticVTable();
-// prototype = nullptr;
-// parent = nullptr;
-// size = 0;
- extensible = true;
- isFrozen = false;
- isSealed = false;
- isUsedAsProto = false;
protoId = engine->newProtoId();
// Also internal classes need an internal class pointer. Simply make it point to itself
@@ -300,10 +300,8 @@ void InternalClass::init(Heap::InternalClass *other)
prototype = other->prototype;
parent = other;
size = other->size;
- extensible = other->extensible;
- isSealed = other->isSealed;
- isFrozen = other->isFrozen;
- isUsedAsProto = other->isUsedAsProto;
+ numRedundantTransitions = other->numRedundantTransitions;
+ flags = other->flags;
protoId = engine->newProtoId();
internalClass.set(engine, other->internalClass);
@@ -365,7 +363,99 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
++newClass->size;
}
-Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
+static PropertyAttributes attributesFromFlags(int flags)
+{
+ PropertyAttributes attributes;
+ attributes.m_all = uchar(flags);
+ return attributes;
+}
+
+static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig)
+{
+ if (++orig->numRedundantTransitions < Heap::InternalClass::MaxRedundantTransitions)
+ return orig;
+
+ // We will generally add quite a few transitions here. We have 255 redundant ones.
+ // We can expect at least as many significant ones in addition.
+ std::vector<InternalClassTransition> transitions;
+
+ Scope scope(orig->engine);
+ Scoped<QV4::InternalClass> child(scope, orig);
+
+ {
+ quint8 remainingRedundantTransitions = orig->numRedundantTransitions;
+ QSet<PropertyKey> properties;
+ int structureChanges = 0;
+
+ Scoped<QV4::InternalClass> parent(scope, orig->parent);
+ while (parent && remainingRedundantTransitions > 0) {
+ Q_ASSERT(child->d() != scope.engine->classes[ExecutionEngine::Class_Empty]);
+ const auto it = std::find_if(
+ parent->d()->transitions.begin(), parent->d()->transitions.end(),
+ [&child](const InternalClassTransition &t) {
+ return child->d() == t.lookup;
+ });
+ Q_ASSERT(it != parent->d()->transitions.end());
+
+ if (it->flags & InternalClassTransition::StructureChange) {
+ // A structural change. Each kind of structural change has to be recorded only once.
+ if ((structureChanges & it->flags) != it->flags) {
+ transitions.push_back(*it);
+ structureChanges |= it->flags;
+ } else {
+ --remainingRedundantTransitions;
+ }
+ } else if (!properties.contains(it->id)) {
+ // We only need the final state of the property.
+ properties.insert(it->id);
+
+ // Property removal creates _two_ redundant transitions.
+ // We don't have to replay either, but numRedundantTransitions only records one.
+ if (it->flags != 0)
+ transitions.push_back(*it);
+ } else {
+ --remainingRedundantTransitions;
+ }
+
+ child = parent->d();
+ parent = child->d()->parent;
+ Q_ASSERT(child->d() != parent->d());
+ }
+ }
+
+ for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) {
+ switch (it->flags) {
+ case InternalClassTransition::NotExtensible:
+ child = child->d()->nonExtensible();
+ continue;
+ case InternalClassTransition::VTableChange:
+ child = child->d()->changeVTable(it->vtable);
+ continue;
+ case InternalClassTransition::PrototypeChange:
+ child = child->d()->changePrototype(it->prototype);
+ continue;
+ case InternalClassTransition::ProtoClass:
+ child = child->d()->asProtoClass();
+ continue;
+ case InternalClassTransition::Sealed:
+ child = child->d()->sealed();
+ continue;
+ case InternalClassTransition::Frozen:
+ child = child->d()->frozen();
+ continue;
+ default:
+ Q_ASSERT(it->flags != 0);
+ Q_ASSERT(it->flags < InternalClassTransition::StructureChange);
+ child = child->addMember(it->id, attributesFromFlags(it->flags));
+ continue;
+ }
+ }
+
+ return child->d();
+}
+
+Heap::InternalClass *InternalClass::changeMember(
+ PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
if (!data.isEmpty())
data.resolve();
@@ -381,7 +471,7 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
}
if (data == propertyData.at(idx))
- return static_cast<Heap::InternalClass *>(this);
+ return this;
Transition temp = { { identifier }, nullptr, int(data.all()) };
Transition &t = lookupOrInsertTransition(temp);
@@ -394,7 +484,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
Q_ASSERT(!propertyData.at(idx).isAccessor());
// add a dummy entry for the accessor
- entry->setterIndex = newClass->size;
+ if (entry)
+ entry->setterIndex = newClass->size;
e->setterIndex = newClass->size;
addDummyEntry(newClass, *e);
}
@@ -403,7 +494,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert
t.lookup = newClass;
Q_ASSERT(t.lookup);
- return newClass;
+
+ return cleanInternalClass(newClass);
}
Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
@@ -413,7 +505,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
- Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
+ Q_ASSERT(!proto || proto->internalClass->isUsedAsProto());
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange };
temp.prototype = proto;
@@ -427,8 +519,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
newClass->prototype = proto;
t.lookup = newClass;
-
- return newClass;
+ return prototype ? cleanInternalClass(newClass) : newClass;
}
Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
@@ -449,12 +540,14 @@ Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
t.lookup = newClass;
Q_ASSERT(t.lookup);
Q_ASSERT(newClass->vtable);
- return newClass;
+ return vtable == QV4::InternalClass::staticVTable()
+ ? newClass
+ : cleanInternalClass(newClass);
}
Heap::InternalClass *InternalClass::nonExtensible()
{
- if (!extensible)
+ if (!isExtensible())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible};
@@ -463,7 +556,7 @@ Heap::InternalClass *InternalClass::nonExtensible()
return t.lookup;
Heap::InternalClass *newClass = engine->newClass(this);
- newClass->extensible = false;
+ newClass->flags |= NotExtensible;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -500,7 +593,7 @@ Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAt
Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
- Transition temp = { { identifier }, nullptr, (int)data.flags() };
+ Transition temp = { { identifier }, nullptr, int(data.all()) };
Transition &t = lookupOrInsertTransition(temp);
if (entry) {
@@ -553,21 +646,23 @@ void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier)
changeMember(object, identifier, Attr_Invalid);
#ifndef QT_NO_DEBUG
- // we didn't remove the data slot, just made it inaccessible
- Q_ASSERT(object->internalClass()->size == oldClass->size);
+ // We didn't remove the data slot, just made it inaccessible.
+ // ... unless we've rebuilt the whole class. Then all the deleted properties are gone.
+ Q_ASSERT(object->internalClass()->numRedundantTransitions == 0
+ || object->internalClass()->size == oldClass->size);
#endif
}
Heap::InternalClass *InternalClass::sealed()
{
- if (isSealed)
+ if (isSealed())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isSealed);
+ Q_ASSERT(t.lookup && t.lookup->isSealed());
return t.lookup;
}
@@ -575,7 +670,7 @@ Heap::InternalClass *InternalClass::sealed()
Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
Heap::InternalClass *s = ic->d();
- if (!isFrozen) { // freezing also makes all properties non-configurable
+ if (!isFrozen()) { // freezing also makes all properties non-configurable
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
@@ -584,7 +679,7 @@ Heap::InternalClass *InternalClass::sealed()
s->propertyData.set(i, attrs);
}
}
- s->isSealed = true;
+ s->flags |= Sealed;
t.lookup = s;
return s;
@@ -592,14 +687,14 @@ Heap::InternalClass *InternalClass::sealed()
Heap::InternalClass *InternalClass::frozen()
{
- if (isFrozen)
+ if (isFrozen())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isFrozen);
+ Q_ASSERT(t.lookup && t.lookup->isFrozen());
return t.lookup;
}
@@ -616,7 +711,7 @@ Heap::InternalClass *InternalClass::frozen()
attrs.setConfigurable(false);
f->propertyData.set(i, attrs);
}
- f->isFrozen = true;
+ f->flags |= Frozen;
t.lookup = f;
return f;
@@ -640,7 +735,7 @@ InternalClass *InternalClass::cryopreserved()
bool InternalClass::isImplicitlyFrozen() const
{
- if (isFrozen)
+ if (isFrozen())
return true;
for (uint i = 0; i < size; ++i) {
@@ -656,7 +751,7 @@ bool InternalClass::isImplicitlyFrozen() const
Heap::InternalClass *InternalClass::asProtoClass()
{
- if (isUsedAsProto)
+ if (isUsedAsProto())
return this;
Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass };
@@ -665,7 +760,7 @@ Heap::InternalClass *InternalClass::asProtoClass()
return t.lookup;
Heap::InternalClass *newClass = engine->newClass(this);
- newClass->isUsedAsProto = true;
+ newClass->flags |= UsedAsProto;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -685,7 +780,7 @@ static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
void InternalClass::updateProtoUsage(Heap::Object *o)
{
- Q_ASSERT(isUsedAsProto);
+ Q_ASSERT(isUsedAsProto());
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
@@ -698,6 +793,9 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
if (ic->prototype)
ic->prototype->mark(stack);
+ if (ic->parent)
+ ic->parent->mark(stack);
+
ic->nameMap.mark(stack);
}
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index c18133f4cd..37dbeded6f 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -173,7 +173,7 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> {
uint size() const { return m_size; }
void setSize(uint s) { m_size = s; }
- PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
+ PropertyAttributes at(uint i) const { Q_ASSERT(data && i < m_alloc); return data[i]; }
void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
void mark(MarkStack *) {}
@@ -198,7 +198,7 @@ struct SharedInternalClassDataPrivate<PropertyKey> {
uint size() const;
void setSize(uint s);
- PropertyKey at(uint i);
+ PropertyKey at(uint i) const;
void set(uint i, PropertyKey t);
void mark(MarkStack *s);
@@ -289,24 +289,33 @@ struct InternalClassTransition
int flags;
enum {
// range 0-0xff is reserved for attribute changes
- NotExtensible = 0x100,
- VTableChange = 0x200,
- PrototypeChange = 0x201,
- ProtoClass = 0x202,
- Sealed = 0x203,
- Frozen = 0x204
+ StructureChange = 0x100,
+ NotExtensible = StructureChange | (1 << 0),
+ VTableChange = StructureChange | (1 << 1),
+ PrototypeChange = StructureChange | (1 << 2),
+ ProtoClass = StructureChange | (1 << 3),
+ Sealed = StructureChange | (1 << 4),
+ Frozen = StructureChange | (1 << 5),
};
bool operator==(const InternalClassTransition &other) const
{ return id == other.id && flags == other.flags; }
bool operator<(const InternalClassTransition &other) const
- { return id < other.id || (id == other.id && flags < other.flags); }
+ { return flags < other.flags || (flags == other.flags && id < other.id); }
};
namespace Heap {
struct InternalClass : Base {
+ enum Flag {
+ NotExtensible = 1 << 0,
+ Sealed = 1 << 1,
+ Frozen = 1 << 2,
+ UsedAsProto = 1 << 3,
+ };
+ enum { MaxRedundantTransitions = 255 };
+
ExecutionEngine *engine;
const VTable *vtable;
quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
@@ -322,10 +331,13 @@ struct InternalClass : Base {
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
uint size;
- bool extensible;
- bool isSealed;
- bool isFrozen;
- bool isUsedAsProto;
+ quint8 numRedundantTransitions;
+ quint8 flags;
+
+ bool isExtensible() const { return !(flags & NotExtensible); }
+ bool isSealed() const { return flags & Sealed; }
+ bool isFrozen() const { return flags & Frozen; }
+ bool isUsedAsProto() const { return flags & UsedAsProto; }
void init(ExecutionEngine *engine);
void init(InternalClass *other);
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c6d320ac20..70ddaf59c5 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -146,10 +146,12 @@ struct ScopedStackFrame {
return;
frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value)));
frame.jsFrame->context = context;
- if (auto *parent = frame.parentFrame())
+ if (auto *parent = frame.parentFrame()) {
frame.v4Function = parent->v4Function;
- else
+ frame.instructionPointer = parent->instructionPointer;
+ } else {
frame.v4Function = nullptr;
+ }
scope.engine->currentStackFrame = &frame;
}
~ScopedStackFrame() {
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index a3ffdb5431..ad9760bfd6 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -646,6 +646,28 @@ struct Stringify
QString makeMember(const QString &key, const Value &v);
};
+class [[nodiscard]] CallDepthAndCycleChecker
+{
+ Q_DISABLE_COPY_MOVE(CallDepthAndCycleChecker);
+
+public:
+ CallDepthAndCycleChecker(Stringify *stringify, Object *o)
+ : m_callDepthRecorder(stringify->v4)
+ {
+ if (stringify->stackContains(o)) {
+ stringify->v4->throwTypeError(
+ QStringLiteral("Cannot convert circular structure to JSON"));
+ }
+
+ stringify->v4->checkStackLimits();
+ }
+
+ bool foundProblem() const { return m_callDepthRecorder.ee->hasException; }
+
+private:
+ ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder;
+};
+
static QString quote(const QString &str)
{
QString product;
@@ -776,10 +798,9 @@ QString Stringify::makeMember(const QString &key, const Value &v)
QString Stringify::JO(Object *o)
{
- if (stackContains(o)) {
- v4->throwTypeError();
+ CallDepthAndCycleChecker check(this, o);
+ if (check.foundProblem())
return QString();
- }
Scope scope(v4);
@@ -836,10 +857,9 @@ QString Stringify::JO(Object *o)
QString Stringify::JA(Object *a)
{
- if (stackContains(a)) {
- v4->throwTypeError();
+ CallDepthAndCycleChecker check(this, a);
+ if (check.foundProblem())
return QString();
- }
Scope scope(a->engine());
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index d3ea50867a..61c0d3daa7 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -41,6 +41,7 @@
#include "qv4jscall_p.h"
#include "qv4string_p.h"
#include <private/qv4identifiertable_p.h>
+#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
@@ -144,47 +145,76 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va
return l->resolvePrimitiveGetter(engine, object);
}
+static inline void setupObjectLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
+{
+ Heap::InternalClass *ic1 = first.objectLookup.ic;
+ const uint offset1 = first.objectLookup.offset;
+ Heap::InternalClass *ic2 = second.objectLookup.ic;
+ const uint offset2 = second.objectLookup.offset;
+
+ l->objectLookupTwoClasses.ic = ic1;
+ l->objectLookupTwoClasses.ic2 = ic2;
+ l->objectLookupTwoClasses.offset = offset1;
+ l->objectLookupTwoClasses.offset2 = offset2;
+}
+
+static inline void setupProtoLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second)
+{
+ const quintptr protoId1 = first.protoLookup.protoId;
+ const Value *data1 = first.protoLookup.data;
+ const quintptr protoId2 = second.protoLookup.protoId;
+ const Value *data2 = second.protoLookup.data;
+
+ l->protoLookupTwoClasses.protoId = protoId1;
+ l->protoLookupTwoClasses.protoId2 = protoId2;
+ l->protoLookupTwoClasses.data = data1;
+ l->protoLookupTwoClasses.data2 = data2;
+}
+
ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (const Object *o = object.as<Object>()) {
- Lookup first = *l;
- Lookup second = *l;
-
- ReturnedValue result = second.resolveGetter(engine, o);
- if (first.getter == getter0Inline && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
- l->objectLookupTwoClasses.ic = first.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = second.objectLookup.ic;
- l->objectLookupTwoClasses.offset = first.objectLookup.offset;
- l->objectLookupTwoClasses.offset2 = second.objectLookup.offset;
- l->getter = second.getter == getter0Inline ? getter0Inlinegetter0Inline : getter0Inlinegetter0MemberData;
+ // Do the resolution on a second lookup, then merge.
+ Lookup second;
+ memset(&second, 0, sizeof(Lookup));
+ second.nameIndex = l->nameIndex;
+ second.getter = getterGeneric;
+ const ReturnedValue result = second.resolveGetter(engine, o);
+
+ if (l->getter == getter0Inline
+ && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
+ setupObjectLookupTwoClasses(l, *l, second);
+ l->getter = (second.getter == getter0Inline)
+ ? getter0Inlinegetter0Inline
+ : getter0Inlinegetter0MemberData;
return result;
}
- if (first.getter == getter0MemberData && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
- l->objectLookupTwoClasses.ic = second.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = first.objectLookup.ic;
- l->objectLookupTwoClasses.offset = second.objectLookup.offset;
- l->objectLookupTwoClasses.offset2 = first.objectLookup.offset;
- l->getter = second.getter == getter0Inline ? getter0Inlinegetter0MemberData : getter0MemberDatagetter0MemberData;
+
+ if (l->getter == getter0MemberData
+ && (second.getter == getter0Inline || second.getter == getter0MemberData)) {
+ setupObjectLookupTwoClasses(l, second, *l);
+ l->getter = (second.getter == getter0Inline)
+ ? getter0Inlinegetter0MemberData
+ : getter0MemberDatagetter0MemberData;
return result;
}
- if (first.getter == getterProto && second.getter == getterProto) {
- l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
- l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
- l->protoLookupTwoClasses.data = first.protoLookup.data;
- l->protoLookupTwoClasses.data2 = second.protoLookup.data;
+
+
+ if (l->getter == getterProto && second.getter == getterProto) {
+ setupProtoLookupTwoClasses(l, *l, second);
l->getter = getterProtoTwoClasses;
return result;
}
- if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
- l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
- l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
- l->protoLookupTwoClasses.data = first.protoLookup.data;
- l->protoLookupTwoClasses.data2 = second.protoLookup.data;
+
+ if (l->getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
+ setupProtoLookupTwoClasses(l, *l, second);
l->getter = getterProtoAccessorTwoClasses;
return result;
}
+ // If any of the above options were true, the propertyCache was inactive.
+ second.releasePropertyCache();
}
l->getter = getterFallback;
@@ -371,7 +401,19 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
+}
+ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qobjectLookup.propertyCache->release();
+ lookup->qobjectLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ return QObjectWrapper::lookupGetterImpl(
+ lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
}
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
@@ -463,23 +505,30 @@ bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, co
bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Lookup first = *l;
- Lookup second = *l;
+ // A precondition of this method is that l->objectLookup is the active variant of the union.
+ Q_ASSERT(l->setter == setter0MemberData || l->setter == setter0Inline);
if (object.isObject()) {
+
+ // As l->objectLookup is active, we can stash some members here, before resolving.
+ Heap::InternalClass *ic = l->objectLookup.ic;
+ const uint index = l->objectLookup.index;
+
if (!l->resolveSetter(engine, static_cast<Object *>(&object), value)) {
l->setter = setterFallback;
return false;
}
if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) {
- l->objectLookupTwoClasses.ic = first.objectLookup.ic;
- l->objectLookupTwoClasses.ic2 = second.objectLookup.ic;
- l->objectLookupTwoClasses.offset = first.objectLookup.index;
- l->objectLookupTwoClasses.offset2 = second.objectLookup.index;
+ l->objectLookupTwoClasses.ic = ic;
+ l->objectLookupTwoClasses.ic2 = ic;
+ l->objectLookupTwoClasses.offset = index;
+ l->objectLookupTwoClasses.offset2 = index;
l->setter = setter0setter0;
return true;
}
+
+ l->releasePropertyCache();
}
l->setter = setterFallback;
@@ -550,6 +599,13 @@ bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, con
return setterFallback(l, engine, object, value);
}
+bool Lookup::setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
+{
+ // This setter just marks the presence of a qobjectlookup. It only does anything with it when
+ // running AOT-compiled code, though.
+ return QV4::Lookup::setterFallback(l, engine, object, v);
+}
+
bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
{
Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index aab6b66597..301dad38fd 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -56,11 +56,16 @@
#include "qv4context_p.h"
#include "qv4object_p.h"
#include "qv4internalclass_p.h"
+#include "qv4qmlcontext_p.h"
+#include <private/qqmltypewrapper_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
+// Note: We cannot hide the copy ctor and assignment operator of this class because it needs to
+// be trivially copyable. But you should never ever copy it. There are refcounted members
+// in there.
struct Q_QML_PRIVATE_EXPORT Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -126,6 +131,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
QQmlPropertyData *propertyData;
} qobjectLookup;
struct {
+ quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc
+ quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
+ int coreIndex;
+ int notifyIndex;
+ } qobjectFallbackLookup;
+ struct {
Heap::InternalClass *ic;
quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial
@@ -191,6 +202,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -208,6 +220,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
void markObjects(MarkStack *stack) {
@@ -220,6 +233,18 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
void clear() {
memset(&markDef, 0, sizeof(markDef));
}
+
+ void releasePropertyCache()
+ {
+ if (getter == getterQObject
+ || getter == QQmlTypeWrapper::lookupSingletonProperty
+ || setter == setterQObject
+ || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
+ || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
+ if (QQmlPropertyCache *pc = qobjectLookup.propertyCache)
+ pc->release();
+ }
+ }
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
@@ -227,6 +252,33 @@ Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
// across 32-bit and 64-bit (matters when cross-compiling).
Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0);
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData)
+{
+ lookup->releasePropertyCache();
+ Q_ASSERT(ddata->propertyCache != nullptr);
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+}
+
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData,
+ const Object *self)
+{
+ lookup->qobjectLookup.ic = self->internalClass();
+ setupQObjectLookup(lookup, ddata, propertyData);
+}
+
+
+inline void setupQObjectLookup(
+ Lookup *lookup, const QQmlData *ddata, QQmlPropertyData *propertyData,
+ const Object *self, const Object *qmlType)
+{
+ lookup->qobjectLookup.qmlTypeIc = qmlType->internalClass();
+ setupQObjectLookup(lookup, ddata, propertyData, self);
+}
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 2ad1ecb6e3..564cd47203 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -61,17 +61,52 @@ DEFINE_OBJECT_VTABLE(Object);
void Object::setInternalClass(Heap::InternalClass *ic)
{
- d()->internalClass.set(engine(), ic);
- if (ic->isUsedAsProto)
- ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
- uint nInline = d()->vtable()->nInlineProperties;
- if (ic->size <= nInline)
- return;
- bool hasMD = d()->memberData != nullptr;
- uint requiredSize = ic->size - nInline;
- if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize))
- d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData));
+ Heap::Object *p = d();
+
+ if (ic->numRedundantTransitions < p->internalClass.get()->numRedundantTransitions) {
+ // IC was rebuilt. The indices are different now. We need to move everything.
+
+ Scope scope(engine());
+
+ // We allocate before setting the new IC. Protect it from GC.
+ Scoped<InternalClass> newIC(scope, ic);
+
+ // Pick the members of the old IC that are still valid in the new IC.
+ // Order them by index in memberData (or inline data).
+ Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size));
+ for (uint i = 0; i < ic->size; ++i)
+ newMembers->set(scope.engine, i, get(ic->nameMap.at(i)));
+
+ p->internalClass.set(scope.engine, ic);
+ const uint nInline = p->vtable()->nInlineProperties;
+
+ if (ic->size > nInline)
+ p->memberData.set(scope.engine, MemberData::allocate(ic->engine, ic->size - nInline));
+ else
+ p->memberData.set(scope.engine, nullptr);
+
+ const auto &memberValues = newMembers->d()->values;
+ for (uint i = 0; i < ic->size; ++i)
+ setProperty(i, memberValues[i]);
+ } else {
+ // The old indices are still the same. No need to move any values.
+ // We may need to re-allocate, though.
+
+ p->internalClass.set(ic->engine, ic);
+ const uint nInline = p->vtable()->nInlineProperties;
+ if (ic->size > nInline) {
+ const uint requiredSize = ic->size - nInline;
+ if ((p->memberData ? p->memberData->values.size : 0) < requiredSize) {
+ p->memberData.set(ic->engine, MemberData::allocate(
+ ic->engine, requiredSize, p->memberData));
+ }
+ }
+ }
+
+ if (ic->isUsedAsProto())
+ ic->updateProtoUsage(p);
+
}
void Object::getProperty(const InternalClassEntry &entry, Property *p) const
@@ -958,7 +993,7 @@ bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property
bool Object::virtualIsExtensible(const Managed *m)
{
- return m->d()->internalClass->extensible;
+ return m->d()->internalClass->isExtensible();
}
bool Object::virtualPreventExtensions(Managed *m)
@@ -982,7 +1017,7 @@ bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
Heap::Object *protod = proto ? proto->d() : nullptr;
if (current == protod)
return true;
- if (!o->internalClass()->extensible)
+ if (!o->internalClass()->isExtensible())
return false;
Heap::Object *p = protod;
while (p) {
@@ -1013,6 +1048,8 @@ bool Object::setArrayLength(uint newLen)
} else {
if (newLen >= 0x100000)
initSparseArray();
+ else
+ ArrayData::realloc(this, arrayType(), newLen, false);
}
setArrayLengthUnchecked(newLen);
return ok;
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index 26e1074fe3..1f518966d6 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -50,8 +50,8 @@ FunctionLocation FunctionCall::resolveLocation() const
{
return FunctionLocation(m_function->name()->toQString(),
m_function->executableCompilationUnit()->fileName(),
- m_function->compiledFunction->location.line,
- m_function->compiledFunction->location.column);
+ m_function->compiledFunction->location.line(),
+ m_function->compiledFunction->location.column());
}
FunctionCallProperties FunctionCall::properties() const
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index 83bfba9b11..e155f2c3ba 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -116,6 +116,8 @@ struct ResolveThenableEvent : public QEvent
} // namespace QV4
QT_END_NAMESPACE
+#include "moc_qv4promiseobject_p.cpp"
+
ReactionHandler::ReactionHandler(QObject *parent)
: QObject(parent)
{}
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index b5dca2d2ac..bb93dc518d 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -51,6 +51,7 @@
//
#include <private/qv4global_p.h>
+#include <QtCore/qhashfunctions.h>
QT_BEGIN_NAMESPACE
@@ -145,6 +146,7 @@ public:
bool operator ==(const PropertyKey &other) const { return val == other.val; }
bool operator !=(const PropertyKey &other) const { return val != other.val; }
bool operator <(const PropertyKey &other) const { return val < other.val; }
+ friend size_t qHash(const PropertyKey &key, size_t seed = 0) { return qHash(key.val, seed); }
};
}
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 65481e4539..21a91342fb 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -215,7 +215,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
if (context->imports() && name->startsWithUpper()) {
// Search for attached properties, enums and imported scripts
- QQmlTypeNameCache::Result r = context->imports()->query(name, QQmlImport::AllowRecursion);
+ QQmlTypeNameCache::Result r = context->imports()->query<QQmlImport::AllowRecursion>(name);
if (r.isValid()) {
if (hasProperty)
@@ -306,11 +306,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
QQmlData *ddata = QQmlData::get(scopeObject, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
- const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
- lookup->qobjectLookup.ic = That->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = propertyData;
+ QV4::setupQObjectLookup(lookup, ddata, propertyData, val->objectValue());
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
}
}
@@ -337,14 +333,12 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
if (propertyData) {
if (lookup) {
QQmlData *ddata = QQmlData::get(contextObject, false);
- if (ddata && ddata->propertyCache) {
+ if (ddata && ddata->propertyCache
+ && lookup->qmlContextPropertyGetter != contextGetterFunction) {
ScopedValue val(scope, base ? *base
: Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, contextObject)));
- const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
- lookup->qobjectLookup.ic = That->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = propertyData;
+ QV4::setupQObjectLookup(lookup, ddata, propertyData,
+ val->objectValue());
lookup->qmlContextPropertyGetter = contextGetterFunction;
}
} else if (originalLookup) {
@@ -643,6 +637,11 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
+ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ return resolveQmlContextPropertyLookupGetter(l, engine, base);
+}
+
ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 19324b8803..253df6536d 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 20ccb00093..8be952571a 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -250,7 +250,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
{
- QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
+ QQmlData::flushPendingBinding(object, property->coreIndex());
if (property->isFunction() && !property->isVarProperty()) {
if (property->isVMEFunction()) {
@@ -275,7 +275,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
if (ep && ep->propertyCapture && !property->isConstant())
- ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
+ if (!property->isBindable() || ep->propertyCapture->expression->mustCaptureBindableProperty())
+ ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
@@ -776,7 +777,7 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
ScopedString name(scope, id.asStringOrSymbol());
- if (that->internalClass()->isFrozen) {
+ if (that->internalClass()->isFrozen()) {
QString error = QLatin1String("Cannot assign to property \"") +
name->toQString() + QLatin1String("\" of read-only object");
scope.engine->throwError(error);
@@ -944,37 +945,17 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
}
- lookup->qobjectLookup.ic = This->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
- lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ QV4::setupQObjectLookup(lookup, ddata, property, This);
+ lookup->getter = QV4::Lookup::getterQObject;
return lookup->getter(lookup, engine, *object);
}
-ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
-{
- const auto revertLookup = [lookup, engine, &object]() {
- lookup->qobjectLookup.propertyCache->release();
- lookup->qobjectLookup.propertyCache = nullptr;
- lookup->getter = Lookup::getterGeneric;
- return Lookup::getterGeneric(lookup, engine, object);
- };
-
- return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
-}
-
ReturnedValue QObjectWrapper::lookupAttached(
Lookup *l, ExecutionEngine *engine, const Value &object)
{
return QV4::Lookup::getterGeneric(l, engine, object);
}
-bool QObjectWrapper::lookupSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
-{
- return QV4::Lookup::setterFallback(l, engine, object, v);
-}
-
bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
@@ -1256,8 +1237,7 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markSta
void QObjectWrapper::destroyObject(bool lastCall)
{
Heap::QObjectWrapper *h = d();
- if (!h->internalClass)
- return; // destroyObject already got called
+ Q_ASSERT(h->internalClass);
if (h->object()) {
QQmlData *ddata = QQmlData::get(h->object(), false);
@@ -1287,7 +1267,7 @@ void QObjectWrapper::destroyObject(bool lastCall)
}
}
- h->~Data();
+ h->destroy();
}
@@ -2012,7 +1992,7 @@ bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, c
qvariantPtr->convert(callMetaType);
} else {
QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
- if (!mo.isNull()) {
+ if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
QObject *obj = QQmlMetaType::toQObject(v);
if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 37cb4d3cac..f7bdda5f6f 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -189,10 +189,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
- static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
- static bool lookupSetter(QV4::Lookup *l, QV4::ExecutionEngine *engine,
- QV4::Value &object, const QV4::Value &value);
template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 2a6c61f044..39a0b03dec 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -76,7 +76,10 @@ struct CallArgs {
static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
{
- int len = o->getLength();
+ int len = scope.engine->safeForAllocLength(o->getLength());
+ if (scope.engine->hasException)
+ return {nullptr, 0};
+
Value *arguments = scope.alloc(len);
for (int i = 0; i < len; ++i) {
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 01a7878dae..f27754f4a3 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -2440,6 +2440,7 @@ QHash<const void *, const char *> Runtime::symbolTable()
{symbol<UMinus>(), "UMinus" },
{symbol<Instanceof>(), "Instanceof" },
+ {symbol<As>(), "As" },
{symbol<In>(), "In" },
{symbol<Add>(), "Add" },
{symbol<Sub>(), "Sub" },
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 0523d740fb..b0268afedd 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -219,7 +219,7 @@ public:
}
loadReference();
}
- if (index < size()) {
+ if (index < quint32(size())) {
if (hasProperty)
*hasProperty = true;
return engine()->fromVariant(at(index));
@@ -251,7 +251,7 @@ public:
loadReference();
}
- qsizetype count = size();
+ quint32 count = quint32(size());
const QMetaType valueMetaType = meta(d())->valueMetaType();
const QVariant element = engine()->toVariant(value, valueMetaType, false);
@@ -284,7 +284,7 @@ public:
return QV4::Attr_Invalid;
loadReference();
}
- return (index < size()) ? QV4::Attr_Data : QV4::Attr_Invalid;
+ return (index < quint32(size())) ? QV4::Attr_Data : QV4::Attr_Invalid;
}
struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
@@ -300,7 +300,7 @@ public:
s->loadReference();
}
- if (arrayIndex < s->size()) {
+ if (arrayIndex < quint32(s->size())) {
uint index = arrayIndex;
++arrayIndex;
if (attrs)
@@ -333,7 +333,7 @@ public:
loadReference();
}
- if (index >= size())
+ if (index >= quint32(size()))
return false;
/* according to ECMA262r3 it should be Undefined, */
@@ -709,3 +709,5 @@ int SequencePrototype::metaTypeForSequence(const QV4::Object *object)
}
QT_END_NAMESPACE
+
+#include "moc_qv4sequenceobject_p.cpp"
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 603d33d6d8..48e4b5ff18 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -61,8 +61,8 @@
#include "qv4string_p.h"
#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qitemselectionmodel.h>
#endif
QT_REQUIRE_CONFIG(qml_sequence_object);
diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp
index a716c53aea..0a060e81a7 100644
--- a/src/qml/jsruntime/qv4stackframe.cpp
+++ b/src/qml/jsruntime/qv4stackframe.cpp
@@ -53,7 +53,7 @@ QString CppStackFrame::function() const
int CppStackFrame::lineNumber() const
{
- if (!v4Function)
+ if (!v4Function || instructionPointer <= 0)
return -1;
auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
@@ -61,9 +61,9 @@ int CppStackFrame::lineNumber() const
};
const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
- uint offset = instructionPointer;
+ const uint offset = instructionPointer;
const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
- uint nLineNumbers = cf->nLineNumbers;
+ const uint nLineNumbers = cf->nLineNumbers;
const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
return line->line;
}
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index d5d11ef660..a55f72e241 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -88,7 +88,7 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
new (&textStorage) QStringPrivate(std::move(text));
}
- mutable std::aligned_storage<sizeof(QStringPrivate), alignof(QStringPrivate)>::type textStorage;
+ mutable struct { alignas(QStringPrivate) unsigned char data[sizeof(QStringPrivate)]; } textStorage;
mutable PropertyKey identifier;
mutable uint subtype;
mutable uint stringHash;
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 94225ff0c7..ab07fe58a6 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -442,8 +442,8 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(function->aotFunction);
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
- function->compiledFunction->location.line,
- function->compiledFunction->location.column);
+ function->compiledFunction->location.line(),
+ function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
const qsizetype numFunctionArguments = function->aotFunction->argumentTypes.size();
@@ -461,9 +461,17 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(argumentType.sizeOf() > 0);
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
- argumentType.construct(arg);
- if (frame->argc() > i)
- QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+
+ if (argumentType == QMetaType::fromType<QVariant>()) {
+ if (frame->argc() > i)
+ new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]);
+ else
+ new (arg) QVariant();
+ } else {
+ argumentType.construct(arg);
+ if (frame->argc() > i)
+ QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+ }
transformedArguments[i] = arg;
}
@@ -472,8 +480,10 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
const QMetaType frameReturn = frame->returnType();
Q_ALLOCA_DECLARE(void, transformedResult);
if (frame->returnValue() && returnType != frameReturn) {
- Q_ASSERT(returnType.sizeOf() > 0);
- Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
+ if (returnType.sizeOf() > 0)
+ Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
+ else
+ transformedResult = frame; // Some non-null marker value
}
QQmlPrivate::AOTCompiledContext aotContext;
@@ -523,8 +533,8 @@ ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)
Function *function = frame->v4Function;
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
- function->compiledFunction->location.line,
- function->compiledFunction->location.column);
+ function->compiledFunction->location.line(),
+ function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 0aeeb0ec5b..b9f06e4133 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -995,7 +995,7 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) {
for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
- if (!it.value().isNullOrUndefined())
+ if (it.value().isNullOrUndefined())
it = multiplyWrappedQObjects->erase(it);
else
++it;
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 670564fea5..43d8474cee 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1722,6 +1722,18 @@ Type: UiQualifiedId;
} break;
./
+Type: T_VAR;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+Type: T_VOID;
+/.
+ case $rule_number: {
+ AST::UiQualifiedId *id = new (pool) AST::UiQualifiedId(stringRef(1));
+ id->identifierToken = loc(1);
+ sym(1).Type = new (pool) AST::Type(id->finish());
+ } break;
+./
+
TypeAnnotation: T_COLON Type;
/.
case $rule_number: {
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index 03955e0563..6bef117646 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -144,7 +144,7 @@ private:
mutable quint32 m_hash = 0;
};
-class Q_AUTOTEST_EXPORT QHashedCStringRef
+class QHashedCStringRef
{
public:
inline QHashedCStringRef();
@@ -157,7 +157,7 @@ public:
inline const char *constData() const;
inline int length() const;
- QString toUtf16() const;
+ Q_AUTOTEST_EXPORT QString toUtf16() const;
inline int utf16length() const;
inline void writeUtf16(QChar *) const;
inline void writeUtf16(quint16 *) const;
diff --git a/src/qml/qml/ftw/qintrusivelist_p.h b/src/qml/qml/ftw/qintrusivelist_p.h
index 8992be9f93..40dc45095f 100644
--- a/src/qml/qml/ftw/qintrusivelist_p.h
+++ b/src/qml/qml/ftw/qintrusivelist_p.h
@@ -241,7 +241,12 @@ typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::end()
template<class N, QIntrusiveListNode N::*member>
N *QIntrusiveList<N, member>::nodeToN(QIntrusiveListNode *node)
{
+ QT_WARNING_PUSH
+#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1300
+ QT_WARNING_DISABLE_CLANG("-Wnull-pointer-subtraction")
+#endif
return (N *)((char *)node - ((char *)&(((N *)nullptr)->*member) - (char *)nullptr));
+ QT_WARNING_POP
}
QIntrusiveListNode::QIntrusiveListNode()
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index ccc90cabbf..1b737ee308 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -344,9 +344,9 @@ static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
return revisions;
const int propertyOffset = metaObject->propertyOffset();
const int propertyCount = metaObject->propertyCount();
- for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
- propertyIndex < propertyEnd; ++propertyIndex) {
- const QMetaProperty property = metaObject->property(propertyIndex);
+ for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
+ coreIndex < propertyEnd; ++coreIndex) {
+ const QMetaProperty property = metaObject->property(coreIndex);
if (int revision = property.revision())
revisions.append(QTypeRevision::fromEncodedVersion(revision));
}
@@ -720,6 +720,21 @@ void AOTCompiledContext::setReturnValueUndefined() const
}
}
+static void captureFallbackProperty(
+ QObject *object, int coreIndex, int notifyIndex, bool isConstant,
+ QQmlContextData *qmlContext)
+{
+ if (!qmlContext || isConstant)
+ return;
+
+ QQmlEngine *engine = qmlContext->engine();
+ Q_ASSERT(engine);
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ Q_ASSERT(ep);
+ if (QQmlPropertyCapture *capture = ep->propertyCapture)
+ capture->captureProperty(object, coreIndex, notifyIndex);
+}
+
static void captureObjectProperty(
QObject *object, const QQmlPropertyCache *propertyCache,
const QQmlPropertyData *property, QQmlContextData *qmlContext)
@@ -746,10 +761,10 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
-static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object, void *target,
- QQmlContextData *qmlContext)
+static ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{
- const QQmlData *qmlData = QQmlData::get(object);
+ QQmlData *qmlData = QQmlData::get(object);
if (!qmlData)
return ObjectPropertyResult::NeedsInit;
if (qmlData->isQueuedForDeletion)
@@ -759,11 +774,43 @@ static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object,
if (!inherits(qmlData->propertyCache, propertyCache))
return ObjectPropertyResult::NeedsInit;
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+
+ const int coreIndex = property->coreIndex();
+ if (qmlData->hasPendingBindingBit(coreIndex))
+ qmlData->flushPendingBinding(coreIndex);
+
captureObjectProperty(object, propertyCache, property, qmlContext);
property->readProperty(object, target);
return ObjectPropertyResult::OK;
}
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+{
+ QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
+ qmlData->flushPendingBinding(coreIndex);
+
+ captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+
+ void *a[] = { target, nullptr };
+ metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{
const QQmlData *qmlData = QQmlData::get(object);
@@ -780,7 +827,67 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object,
return ObjectPropertyResult::OK;
}
-static bool initObjectLookup(
+static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ const QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return ObjectPropertyResult::Deleted;
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return ObjectPropertyResult::NeedsInit;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
+
+ void *args[] = { value, nullptr };
+ metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
+ return ObjectPropertyResult::OK;
+}
+
+static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType,
+ const AOTCompiledContext *aotContext)
+{
+ if (!lookupType.isValid()) {
+ // If type is invalid, then the calling code depends on the lookup
+ // to be set up in order to query the type, via lookupResultMetaType.
+ // We cannot verify the type in this case.
+ } else if ((lookupType.flags() & QMetaType::IsQmlList)
+ && (propertyType.flags() & QMetaType::IsQmlList)) {
+ // We want to check the value types here, but we cannot easily do it.
+ // Internally those are all QObject* lists, though.
+ } else if (lookupType.flags() & QMetaType::PointerToQObject) {
+ // We accept any base class as type, too
+
+ const QMetaObject *typeMetaObject = lookupType.metaObject();
+ const QMetaObject *foundMetaObject = propertyType.metaObject();
+ if (!foundMetaObject) {
+ if (QQmlEngine *engine = aotContext->qmlEngine()) {
+ foundMetaObject = QQmlEnginePrivate::get(engine)->metaObjectForType(
+ propertyType.id()).metaObject();
+ }
+ }
+
+ while (foundMetaObject && foundMetaObject != typeMetaObject)
+ foundMetaObject = foundMetaObject->superClass();
+
+ if (!foundMetaObject)
+ return false;
+ } else if (propertyType != lookupType) {
+ return false;
+ }
+ return true;
+}
+
+enum class ObjectLookupResult {
+ Failure,
+ Object,
+ Fallback
+};
+
+static ObjectLookupResult initObjectLookup(
const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
{
QV4::Scope scope(aotContext->engine->handle());
@@ -797,7 +904,7 @@ static bool initObjectLookup(
QQmlData *ddata = QQmlData::get(object, true);
Q_ASSERT(ddata);
if (ddata->isQueuedForDeletion)
- return false;
+ return ObjectLookupResult::Failure;
QQmlPropertyData *property;
if (!ddata->propertyCache) {
@@ -808,50 +915,36 @@ static bool initObjectLookup(
name.getPointer(), object, aotContext->qmlContext);
}
- if (!property)
- return false;
-
- const QMetaType propType = property->propType();
- if (!type.isValid()) {
- // If type is invalid, then the calling code depends on the lookup
- // to be set up in order to query the type, via lookupResultMetaType.
- // We cannot verify the type in this case.
- } else if ((type.flags() & QMetaType::IsQmlList) && (propType.flags() & QMetaType::IsQmlList)) {
- // We want to check the value types here, but we cannot easily do it.
- // Internally those are all QObject* lists, though.
- } else if (type.flags() & QMetaType::PointerToQObject) {
- // We accept any base class as type, too
-
- const QMetaObject *typeMetaObject = type.metaObject();
- const QMetaObject *foundMetaObject = propType.metaObject();
- if (!foundMetaObject) {
- if (QQmlEngine *engine = aotContext->qmlEngine()) {
- foundMetaObject = QQmlEnginePrivate::get(engine)->metaObjectForType(
- propType.id()).metaObject();
- }
- }
-
- while (foundMetaObject) {
- if (foundMetaObject == typeMetaObject)
- break;
- foundMetaObject = foundMetaObject->superClass();
- }
-
- if (!foundMetaObject)
- return false;
- } else if (propType != type) {
- return false;
+ if (!property) {
+ const QMetaObject *metaObject = object->metaObject();
+ if (!metaObject)
+ return ObjectLookupResult::Failure;
+
+ const int coreIndex = metaObject->indexOfProperty(
+ name->toQStringNoThrow().toUtf8().constData());
+ if (coreIndex < 0)
+ return ObjectLookupResult::Failure;
+
+ const QMetaProperty property = metaObject->property(coreIndex);
+ if (!isTypeCompatible(type, property.metaType(), aotContext))
+ return ObjectLookupResult::Failure;
+
+ l->releasePropertyCache();
+ // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
+ l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
+ l->qobjectFallbackLookup.coreIndex = coreIndex;
+ l->qobjectFallbackLookup.notifyIndex = property.notifySignalIndex();
+ l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
+ return ObjectLookupResult::Fallback;
}
- Q_ASSERT(ddata->propertyCache);
+ if (!isTypeCompatible(type, property->propType(), aotContext))
+ return ObjectLookupResult::Failure;
- if (l->qobjectLookup.propertyCache)
- l->qobjectLookup.propertyCache->release();
+ Q_ASSERT(ddata->propertyCache);
- l->qobjectLookup.propertyCache = ddata->propertyCache;
- l->qobjectLookup.propertyCache->addref();
- l->qobjectLookup.propertyData = property;
- return true;
+ QV4::setupQObjectLookup(l, ddata, property);
+ return ObjectLookupResult::Object;
}
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@@ -887,27 +980,46 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
return false;
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty
- && l->getter != QV4::QObjectWrapper::lookupGetter) {
- return false;
+ if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::Lookup::getterQObject) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(object, property->coreIndex());
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- captureObjectProperty(
- object, l->qobjectLookup.propertyCache, l->qobjectLookup.propertyData, qmlContext);
- return true;
+ if (l->getter == QV4::Lookup::getterFallback) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(object, coreIndex);
+ captureFallbackProperty(
+ object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+ return false;
}
bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty
- && l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupContextObjectProperty) {
- return false;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
+ return true;
}
- captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache,
- l->qobjectLookup.propertyData, qmlContext);
- return true;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
+ captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, qmlContext);
+ return true;
+ }
+
+ return false;
}
QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
@@ -916,7 +1028,8 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
|| l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
- || l->getter == QV4::QObjectWrapper::lookupGetter) {
+ || l->getter == QV4::Lookup::getterQObject
+ || l->setter == QV4::Lookup::setterQObject) {
return l->qobjectLookup.propertyData->propType();
} else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
return QMetaType(l->qgadgetLookup.metaType);
@@ -927,6 +1040,14 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
|| l->getter == QV4::QObjectWrapper::lookupAttached) {
return QMetaType::fromType<QObject *>();
+ } else if (l->getter == QV4::Lookup::getterFallback
+ || l->setter == QV4::Lookup::setterFallback
+ || l->qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ return metaObject->property(coreIndex).metaType();
}
return QMetaType();
}
@@ -940,21 +1061,50 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
QV4::Lookup l;
l.clear();
l.nameIndex = nameIndex;
- if (initObjectLookup(this, &l, qmlScopeObject, type)) {
- switch (storeObjectProperty(&l, qmlScopeObject, value)) {
- case ObjectPropertyResult::NeedsInit:
- engine->handle()->throwTypeError();
- break;
- case ObjectPropertyResult::Deleted:
- engine->handle()->throwTypeError(
- QStringLiteral("Value is null and could not be converted to an object"));
- break;
- case ObjectPropertyResult::OK:
- break;
+ ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
+ switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::Object: {
+ const QMetaType propType = l.qobjectLookup.propertyData->propType();
+ if (type == propType) {
+ storeResult = storeObjectProperty(&l, qmlScopeObject, value);
+ } else {
+ QVariant var(propType);
+ propType.convert(type, value, propType, var.data());
+ storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
+
l.qobjectLookup.propertyCache->release();
- } else {
+ break;
+ }
+ case ObjectLookupResult::Fallback: {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
+ const QMetaType propType
+ = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
+ if (type == propType) {
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
+ } else {
+ QVariant var(propType);
+ propType.convert(type, value, propType, var.data());
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
+ }
+ break;
+ }
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ return;
+ }
+
+ switch (storeResult) {
+ case ObjectPropertyResult::NeedsInit:
engine->handle()->throwTypeError();
+ break;
+ case ObjectPropertyResult::Deleted:
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ break;
+ case ObjectPropertyResult::OK:
+ break;
}
}
@@ -1112,10 +1262,16 @@ void AOTCompiledContext::initLoadGlobalLookup(uint index) const
bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, qmlScopeObject, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::NeedsInit:
return false;
case ObjectPropertyResult::Deleted:
@@ -1136,12 +1292,22 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
QV4::ExecutionEngine *v4 = engine->handle();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (v4->hasException)
+ if (v4->hasException) {
amendException(v4);
- else if (initObjectLookup(this, l, qmlScopeObject, type))
+ return;
+ }
+
+ switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
- else
+ break;
+ case ObjectLookupResult::Fallback:
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
+ break;
+ case ObjectLookupResult::Failure:
v4->throwTypeError();
+ break;
+ }
}
bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
@@ -1206,7 +1372,7 @@ void AOTCompiledContext::initLoadAttachedLookup(uint index, QObject *object) con
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
QV4::Scope scope(engine->handle());
QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
- QQmlTypeNameCache::Result r = qmlContext->imports()->query(name);
+ QQmlTypeNameCache::Result r = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name);
if (!r.isValid() || !r.type.isValid()) {
scope.engine->throwTypeError();
@@ -1235,10 +1401,15 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
if (!object)
return doThrow();
- if (l->getter != QV4::QObjectWrapper::lookupGetter)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = loadObjectProperty(l, object, target, qmlContext);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = loadFallbackProperty(l, object, target, qmlContext);
+ else
return false;
- switch (loadObjectProperty(l, object, target, qmlContext)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1258,10 +1429,17 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
- l->getter = QV4::QObjectWrapper::lookupGetter;
- else
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
+ l->getter = QV4::Lookup::getterQObject;
+ break;
+ case ObjectLookupResult::Fallback:
+ l->getter = QV4::Lookup::getterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
@@ -1310,7 +1488,12 @@ void AOTCompiledContext::initGetEnumLookup(
{
Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- Q_ASSERT(metaObject);
+ if (!metaObject) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of undefined")
+ .arg(QString::fromUtf8(enumValue)));
+ return;
+ }
const int enumIndex = metaObject->indexOfEnumerator(enumerator);
const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
l->qmlEnumValueLookup.encodedEnumValue = value;
@@ -1329,10 +1512,15 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
return doThrow();
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->setter != QV4::QObjectWrapper::lookupSetter)
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->setter == QV4::Lookup::setterQObject)
+ result = storeObjectProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallback)
+ result = storeFallbackProperty(l, object, value);
+ else
return false;
- switch (storeObjectProperty(l, object, value)) {
+ switch (result) {
case ObjectPropertyResult::Deleted:
return doThrow();
case ObjectPropertyResult::NeedsInit:
@@ -1352,10 +1540,17 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
amendException(v4);
} else {
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (initObjectLookup(this, l, object, type))
- l->setter = QV4::QObjectWrapper::lookupSetter;
- else
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
+ l->setter = QV4::Lookup::setterQObject;
+ break;
+ case ObjectLookupResult::Fallback:
+ l->setter = QV4::Lookup::setterFallback;
+ break;
+ case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
+ break;
+ }
}
}
diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h
index c999b9dce7..a83d4f3752 100644
--- a/src/qml/qml/qqmlanybinding_p.h
+++ b/src/qml/qml/qqmlanybinding_p.h
@@ -149,6 +149,30 @@ public:
/*!
\internal
+ Creates a binding for property \a prop from \a script.
+ \a obj is the scope object which shall be used for the function and \a ctxt its QML scope.
+ The binding is not installed on the property (but if a QQmlBinding is created, it has its
+ target set to \a prop).
+ */
+ static QQmlAnyBinding createFromScriptString(const QQmlProperty &prop, const QQmlScriptString &script,
+ QObject *obj, QQmlContext *ctxt)
+ {
+ QQmlAnyBinding binding;
+ auto propPriv = QQmlPropertyPrivate::get(prop);
+ if (prop.isBindable()) {
+ auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
+ binding = QQmlPropertyBinding::createFromScriptString(&propPriv->core, script, obj, ctxt, prop.object(), index);
+ } else {
+ auto qmlBinding = QQmlBinding::create(&propPriv->core, script, obj, ctxt);
+ qmlBinding->setTarget(prop);
+ binding = qmlBinding;
+ }
+ return binding;
+ }
+
+
+ /*!
+ \internal
Removes the binding from \a prop if there is any.
*/
static void removeBindingFrom(QQmlProperty &prop)
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index e0ab25892f..83323a7087 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -73,7 +73,9 @@ void QQmlApplicationEnginePrivate::init()
&QCoreApplication::quit, Qt::QueuedConnection);
q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(),
&QCoreApplication::exit, Qt::QueuedConnection);
- q->connect(q, SIGNAL(uiLanguageChanged()), q_func(), SLOT(_q_loadTranslations()));
+ QObject::connect(q, &QJSEngine::uiLanguageChanged, q, [this](){
+ _q_loadTranslations();
+ });
#if QT_CONFIG(translation)
QTranslator* qtTranslator = new QTranslator(q);
if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::path(QLibraryInfo::TranslationsPath), QLatin1String(".qm")))
@@ -89,11 +91,10 @@ void QQmlApplicationEnginePrivate::init()
void QQmlApplicationEnginePrivate::_q_loadTranslations()
{
#if QT_CONFIG(translation)
+ Q_Q(QQmlApplicationEngine);
if (translationsDirectory.isEmpty())
return;
- Q_Q(QQmlApplicationEngine);
-
QScopedPointer<QTranslator> translator(new QTranslator);
if (!uiLanguage.value().isEmpty()) {
QLocale locale(uiLanguage);
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 62275c792e..0b5081d491 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -394,19 +394,26 @@ protected:
}
};
-class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
+class QQmlTranslationBinding : public GenericBinding<QMetaType::QString>, public QPropertyObserver {
public:
QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+ : QPropertyObserver(&QQmlTranslationBinding::onLanguageChange)
{
setCompilationUnit(compilationUnit);
m_binding = binding;
+ setSource(QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage);
}
QQmlSourceLocation sourceLocation() const override final
{
- return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
+ return QQmlSourceLocation(
+ m_compilationUnit->fileName(), m_binding->valueLocation.line(),
+ m_binding->valueLocation.column());
}
+ static void onLanguageChange(QPropertyObserver *observer, QUntypedPropertyData *)
+ { static_cast<QQmlTranslationBinding *>(observer)->update(); }
+
void doUpdate(const DeleteWatcher &watcher,
QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
{
@@ -528,8 +535,12 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
value = v4engine->toVariant(result, QMetaType::fromType<QList<QObject *> >());
} else if (result.isNull() && core.isQObject()) {
value = QVariant::fromValue((QObject *)nullptr);
- } else if (core.propType().id() == qMetaTypeId<QList<QUrl> >()) {
- value = QQmlPropertyPrivate::urlSequence(v4engine->toVariant(result, QMetaType::fromType<QList<QUrl>>()));
+ } else if (core.propType() == QMetaType::fromType<QList<QUrl>>()) {
+ const QVariant resultVariant
+ = v4engine->toVariant(result, QMetaType::fromType<QList<QUrl>>());
+ value = QVariant::fromValue(QQmlPropertyPrivate::resolveUrlsOnAssignment()
+ ? QQmlPropertyPrivate::urlSequence(resultVariant, context())
+ : QQmlPropertyPrivate::urlSequence(resultVariant));
} else if (!isVarProperty && metaType != QMetaType::fromType<QJSValue>()) {
value = v4engine->toVariant(result, metaType);
}
@@ -680,14 +691,34 @@ bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
valueType ? valueType->coreIndex() : -1);
}
-bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
+static const QQmlPropertyData *getObjectPropertyData(QObject *object, int coreIndex)
{
- m_target = object;
+ QQmlData *data = QQmlData::get(object, true);
+ if (!data)
+ return nullptr;
+ if (!data->propertyCache) {
+ data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
+ if (!data->propertyCache)
+ return nullptr;
+ data->propertyCache->addref();
+ }
+ const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
+ Q_ASSERT(propertyData);
+ return propertyData;
+}
- if (!object) {
+bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
+{
+ auto invalidate = [this]() {
+ m_target = nullptr;
m_targetIndex = QQmlPropertyIndex();
return false;
- }
+ };
+
+ if (!object)
+ return invalidate();
+
+ m_target = object;
for (bool isAlias = coreIsAlias; isAlias;) {
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
@@ -695,21 +726,25 @@ bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, in
int aValueTypeIndex;
if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
// can't resolve id (yet)
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
+ return invalidate();
}
- if (valueTypeIndex == -1)
- valueTypeIndex = aValueTypeIndex;
- QQmlData *data = QQmlData::get(object, false);
- if (!data || !data->propertyCache) {
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
+ const QQmlPropertyData *propertyData = getObjectPropertyData(object, coreIndex);
+ if (!propertyData)
+ return invalidate();
+ if (aValueTypeIndex != -1) {
+ if (propertyData->propType().flags().testFlag(QMetaType::PointerToQObject)) {
+ // deep alias
+ propertyData->readProperty(object, &object);
+ coreIndex = aValueTypeIndex;
+ valueTypeIndex = -1;
+ propertyData = getObjectPropertyData(object, coreIndex);
+ if (!propertyData)
+ return invalidate();
+ } else {
+ valueTypeIndex = aValueTypeIndex;
+ }
}
- QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
- Q_ASSERT(propertyData);
m_target = object;
isAlias = propertyData->isAlias();
@@ -772,17 +807,23 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
for (int i = 0; i < senderMeta->propertyCount(); i++) {
QMetaProperty property = senderMeta->property(i);
if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
- dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
+ dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(property.name())));
}
}
}
+ for (auto trigger = qpropertyChangeTriggers; trigger; trigger = trigger->next) {
+ QMetaProperty prop = trigger->property();
+ if (prop.isValid())
+ dependencies.push_back(QQmlProperty(trigger->target, QString::fromUtf8(prop.name())));
+ }
+
return dependencies;
}
bool QQmlBinding::hasDependencies() const
{
- return !activeGuards.isEmpty() || translationsCaptured() || qpropertyChangeTriggers;
+ return !activeGuards.isEmpty() || qpropertyChangeTriggers;
}
class QObjectPointerBinding: public QQmlNonbindingBinding
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 4e824fff03..7c3dab98c2 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -97,6 +97,8 @@ public:
~QQmlBinding() override;
+ bool mustCaptureBindableProperty() const final {return true;}
+
void setTarget(const QQmlProperty &);
bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType);
bool setTarget(QObject *, int coreIndex, bool coreIsAlias, int valueTypeIndex);
@@ -131,6 +133,7 @@ public:
* Call this method from the UI thread.
*/
QVector<QQmlProperty> dependencies() const;
+ // This method is used internally to check whether a binding is constant and can be removed
virtual bool hasDependencies() const;
protected:
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index 665d4e6c75..13ed3cf95b 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -114,12 +114,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
QV4::ExecutionEngine *engine = ctxt->engine()->handle();
- // If the function is marked as having a nested function, then the user wrote:
- // onSomeSignal: function() { /*....*/ }
- // So take that nested function:
- if (auto closure = function->nestedFunction()) {
- function = closure;
- } else {
+ if (!function->isClosureWrapper()) {
QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
if (!signalParameters.isEmpty()) {
QString error;
@@ -136,7 +131,29 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
QV4::Scoped<QV4::QmlContext> qmlContext(valueScope, scope);
if (!qmlContext)
qmlContext = QV4::QmlContext::create(engine->rootContext(), ctxt, scopeObject);
- setupFunction(qmlContext, function);
+ if (auto closure = function->nestedFunction()) {
+ // If the function is marked as having a nested function, then the user wrote:
+ // onSomeSignal: function() { /*....*/ }
+ // So take that nested function:
+ setupFunction(qmlContext, closure);
+ } else {
+ setupFunction(qmlContext, function);
+
+ // If it's a closure wrapper but we cannot directly access the nested function
+ // we need to run the outer function to get the nested one.
+ if (function->isClosureWrapper()) {
+ bool isUndefined = false;
+ QV4::ScopedFunctionObject result(
+ valueScope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+
+ Q_ASSERT(!isUndefined);
+ Q_ASSERT(result->function());
+ Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
+
+ QV4::Scoped<QV4::ExecutionContext> callContext(valueScope, result->scope());
+ setupFunction(callContext, result->function());
+ }
+ }
}
void QQmlBoundSignalExpression::init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope)
diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h
index 0a3229b731..89635dec69 100644
--- a/src/qml/qml/qqmlboundsignal_p.h
+++ b/src/qml/qml/qqmlboundsignal_p.h
@@ -80,6 +80,8 @@ public:
// evaluation of a bound signal expression doesn't return any value
void evaluate(void **a);
+ bool mustCaptureBindableProperty() const final {return true;}
+
QString expression() const;
const QObject *target() const { return m_target; }
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 64698444ff..80f8c88973 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -159,11 +159,12 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
{
// ...
component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
- if (component->isLoading())
- QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
- this, SLOT(continueLoading()));
- else
+ if (component->isLoading()) {
+ QObject::connect(component, &QQmlComponent::statusChanged,
+ this, &MyApplication::continueLoading);
+ } else {
continueLoading();
+ }
}
void MyApplication::continueLoading()
@@ -304,7 +305,7 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
\value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
- This is not always possible; for example, remote URLs will always load asynchronously.
+ This is not always possible; for example, remote URLs will always load asynchronously.
\value Asynchronous Load/compile the component in a background thread.
*/
@@ -344,6 +345,7 @@ void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data
RequiredProperties &QQmlComponentPrivate::requiredProperties()
{
+ Q_ASSERT(state.creator);
return state.creator->requiredProperties();
}
@@ -417,7 +419,9 @@ QQmlComponent::~QQmlComponent()
qWarning().nospace().noquote() << QLatin1String(" ") << error;
}
- d->completeCreate();
+ // we might not have the creator anymore if the engine is gone
+ if (d->state.creator)
+ d->completeCreate();
}
if (d->typeData) {
@@ -428,14 +432,16 @@ QQmlComponent::~QQmlComponent()
/*!
\qmlproperty enumeration Component::status
+
This property holds the status of component loading. The status can be one of the
following:
+
\list
- \li Component.Null - no data is available for the component
- \li Component.Ready - the component has been loaded, and can be used to create instances.
- \li Component.Loading - the component is currently being loaded
- \li Component.Error - an error occurred while loading the component.
- Calling errorString() will provide a human-readable description of any errors.
+ \li \c{Component.Null} - no data is available for the component
+ \li \c{Component.Ready} - the component has been loaded, and can be used to create instances.
+ \li \c{Component.Loading} - the component is currently being loaded
+ \li \c{Component.Error} - an error occurred while loading the component.
+ Calling \l errorString() will provide a human-readable description of any errors.
\endlist
*/
@@ -766,7 +772,7 @@ QList<QQmlError> QQmlComponent::errors() const
/*!
\internal
- errorString is only meant as a way to get the errors in script
+ errorString() is only meant as a way to get the errors from QML side.
*/
QString QQmlComponent::errorString() const
{
@@ -807,9 +813,8 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
}
/*!
- Create an object instance from this component. Returns \nullptr if creation
- failed. \a context specifies the context within which to create the object
- instance.
+ Create an object instance from this component, within the specified \a context.
+ Returns \nullptr if creation failed.
If \a context is \nullptr (the default), it will create the instance in the
\l {QQmlEngine::rootContext()}{root context} of the engine.
@@ -828,8 +833,16 @@ QObject *QQmlComponent::create(QQmlContext *context)
Q_D(QQmlComponent);
QObject *rv = d->doBeginCreate(this, context);
- if (rv)
+ if (rv) {
completeCreate();
+ } else if (d->state.completePending) {
+ // overridden completCreate might assume that
+ // the object has actually been created
+ ++creationDepth.localData();
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->engine);
+ d->complete(ep, &d->state);
+ --creationDepth.localData();
+ }
if (rv && !d->requiredProperties().empty()) {
delete rv;
return nullptr;
@@ -838,9 +851,8 @@ QObject *QQmlComponent::create(QQmlContext *context)
}
/*!
- Create an object instance of this component, and initialize its toplevel
- properties with \a initialProperties. \a context specifies the context
- where the object instance is to be created.
+ Create an object instance of this component, within the specified \a context,
+ and initialize its top-level properties with \a initialProperties.
\omit
TODO: also mention errorString() when QTBUG-93239 is fixed
@@ -870,20 +882,21 @@ QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialPr
}
/*!
- This method provides advanced control over component instance creation.
+ Create an object instance from this component, within the specified \a context.
+ Returns \nullptr if creation failed.
+
+ \note This method provides advanced control over component instance creation.
In general, programmers should use QQmlComponent::create() to create object
instances.
- Create an object instance from this component. Returns \nullptr if creation
- failed. \a publicContext specifies the context within which to create the object
- instance.
-
When QQmlComponent constructs an instance, it occurs in three steps:
+
\list 1
\li The object hierarchy is created, and constant values are assigned.
\li Property bindings are evaluated for the first time.
\li If applicable, QQmlParserStatus::componentComplete() is called on objects.
\endlist
+
QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
only performs step 1. QQmlComponent::completeCreate() must be called to
complete steps 2 and 3.
@@ -896,20 +909,21 @@ QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialPr
\sa completeCreate(), QQmlEngine::ObjectOwnership
*/
-QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
+QObject *QQmlComponent::beginCreate(QQmlContext *context)
{
Q_D(QQmlComponent);
- Q_ASSERT(publicContext);
- return d->beginCreate(QQmlContextData::get(publicContext));
+ Q_ASSERT(context);
+ return d->beginCreate(QQmlContextData::get(context));
}
QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context)
{
Q_Q(QQmlComponent);
auto cleanup = qScopeGuard([this] {
- if (!state.errors.isEmpty()) {
- for (const auto &e : qAsConst(state.errors))
+ if (!state.errors.isEmpty() && lcQmlComponentGeneral().isDebugEnabled()) {
+ for (const auto &e : qAsConst(state.errors)) {
qCDebug(lcQmlComponentGeneral) << "QQmlComponent: " << e.toString();
+ }
}
});
if (!context) {
@@ -961,8 +975,8 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
if (rv) {
QQmlData *ddata = QQmlData::get(rv);
Q_ASSERT(ddata);
- //top level objects should never get JS ownership.
- //if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
+ // top-level objects should never get JS ownership.
+ // if JS ownership is needed this needs to be explicitly undone (like in createObject())
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
ddata->rootObjectInCreation = false;
@@ -1022,20 +1036,20 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS
}
/*!
- * \internal
- * Finds the matching toplevel property with name \a name of the component \a createdComponent.
- * If it was a required property or an alias to a required property contained in \a
- * requiredProperties, it is removed from it.
- *
- * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
- * was found in requiredProperties.
- *
- * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
- * for further processing (for instance, actually setting the property value).
- *
- * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
- * classes which create components should not need it and should only need to call
- * setInitialProperties.
+ \internal
+ Finds the matching top-level property with name \a name of the component \a createdComponent.
+ If it was a required property or an alias to a required property contained in \a
+ requiredProperties, it is removed from it.
+
+ If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
+ was found in requiredProperties.
+
+ Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
+ for further processing (for instance, actually setting the property value).
+
+ Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
+ classes which create components should not need it and should only need to call
+ setInitialProperties.
*/
QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties)
{
@@ -1146,14 +1160,14 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
\a incubator. \a context specifies the context within which to create the object
instance.
- If \a context is 0 (the default), it will create the instance in the
+ If \a context is \nullptr (by default), it will create the instance in the
engine's \l {QQmlEngine::rootContext()}{root context}.
\a forContext specifies a context that this object creation depends upon.
If the \a forContext is being created asynchronously, and the
\l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
- this object will also be created asynchronously. If \a forContext is 0
- (the default), the \a context will be used for this decision.
+ this object will also be created asynchronously.
+ If \a forContext is \nullptr (by default), the \a context will be used for this decision.
The created object and its creation status are available via the
\a incubator.
@@ -1201,8 +1215,7 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlC
}
/*!
- Set toplevel \a properties of the \a component.
-
+ Set top-level \a properties of the \a component.
This method provides advanced control over component instance creation.
In general, programmers should use
@@ -1339,7 +1352,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
}
/*!
- \qmlmethod object Component::createObject(QtObject parent, object properties)
+ \qmlmethod QtObject Component::createObject(QtObject parent, object properties)
Creates and returns an object instance of this component that will have
the given \a parent and \a properties. The \a properties argument is optional.
@@ -1368,9 +1381,10 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
below creates an object with initial \c x and \c y values of 100 and 100, respectively:
\js
- var component = Qt.createComponent("Button.qml");
- if (component.status == Component.Ready)
- component.createObject(parent, {x: 100, y: 100});
+ const component = Qt.createComponent("Button.qml");
+ if (component.status === Component.Ready) {
+ component.createObject(parent, { x: 100, y: 100 });
+ }
\endjs
Dynamically created instances can be deleted with the \c destroy() method.
@@ -1408,10 +1422,18 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
break;
}
}
- if (engine->hasException || !object) {
+ if (engine->hasException) {
qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
continue;
}
+ if (!object) {
+ QQmlError error;
+ error.setUrl(qmlContext ? qmlContext->qmlContext()->url() : QUrl());
+ error.setDescription(QLatin1String("Cannot resolve property \"%1\".")
+ .arg(properties.join(u'.')));
+ qmlWarning(createdComponent, error);
+ continue;
+ }
name = engine->newString(properties.last());
object->put(name, val);
if (engine->hasException) {
@@ -1446,8 +1468,10 @@ QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredP
}
error.setDescription(description);
error.setUrl(unsetRequiredProperty.fileUrl);
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ unsetRequiredProperty.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ unsetRequiredProperty.location.column()));
return error;
}
@@ -1521,7 +1545,7 @@ void QQmlComponent::createObject(QQmlV4Function *args)
}
/*!
- \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode)
+ \qmlmethod object Component::incubateObject(QtObject parent, object properties, enumeration mode)
Creates an incubator for an instance of this component. Incubators allow new component
instances to be instantiated asynchronously and do not cause freezes in the UI.
@@ -1543,29 +1567,29 @@ void QQmlComponent::createObject(QQmlV4Function *args)
properties:
\list
- \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
+ \li \c status - The status of the incubator. Valid values are Component.Ready, Component.Loading and
Component.Error.
- \li object The created object instance. Will only be available once the incubator is in the
+ \li \c object - The created object instance. Will only be available once the incubator is in the
Ready status.
- \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
+ \li \c onStatusChanged - Specifies a callback function to be invoked when the status changes. The
status is passed as a parameter to the callback.
- \li forceCompletion() Call to complete incubation synchronously.
+ \li \c{forceCompletion()} - Call to complete incubation synchronously.
\endlist
The following example demonstrates how to use an incubator:
\js
- var component = Qt.createComponent("Button.qml");
+ const component = Qt.createComponent("Button.qml");
- var incubator = component.incubateObject(parent, { x: 10, y: 10 });
- if (incubator.status != Component.Ready) {
+ const incubator = component.incubateObject(parent, { x: 10, y: 10 });
+ if (incubator.status !== Component.Ready) {
incubator.onStatusChanged = function(status) {
- if (status == Component.Ready) {
- print ("Object", incubator.object, "is now ready!");
+ if (status === Component.Ready) {
+ print("Object", incubator.object, "is now ready!");
}
- }
+ };
} else {
- print ("Object", incubator.object, "is ready immediately!");
+ print("Object", incubator.object, "is ready immediately!");
}
\endjs
@@ -1785,3 +1809,4 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
QT_END_NAMESPACE
#include "moc_qqmlcomponent.cpp"
+#include "moc_qqmlcomponentattached_p.cpp"
diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h
index b94d474d1f..2e63289c2d 100644
--- a/src/qml/qml/qqmlcomponentattached_p.h
+++ b/src/qml/qml/qqmlcomponentattached_p.h
@@ -58,6 +58,7 @@
QT_BEGIN_NAMESPACE
+// implemented in qqmlcomponent.cpp
class Q_QML_PRIVATE_EXPORT QQmlComponentAttached : public QObject
{
Q_OBJECT
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index b6b90f13dc..5821aca595 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -76,11 +76,11 @@ QT_BEGIN_NAMESPACE
context->setContextProperty("myModel", &modelData);
QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
+ component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
QObject *window = component.create(context);
\endcode
- Note it is the responsibility of the creator to delete any QQmlContext it
+ \note It is the responsibility of the creator to delete any QQmlContext it
constructs. If the \c context object in the example is no longer needed when the
\c window component instance is destroyed, the \c context must be destroyed explicitly.
The simplest way to ensure this is to set \c window as the parent of \c context.
@@ -96,10 +96,10 @@ QT_BEGIN_NAMESPACE
object.
\code
- class MyDataSet : ... {
- ...
+ class MyDataSet : public QObject {
+ // ...
Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
- ...
+ // ...
};
MyDataSet myDataSet;
@@ -108,7 +108,7 @@ QT_BEGIN_NAMESPACE
context->setContextObject(&myDataSet);
QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
+ component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
component.create(context);
\endcode
@@ -132,10 +132,10 @@ QT_BEGIN_NAMESPACE
QQmlContext *context1 = new QQmlContext(engine.rootContext());
QQmlContext *context2 = new QQmlContext(context1);
- context1->setContextProperty("a", 12);
- context1->setContextProperty("b", 12);
+ context1->setContextProperty("a", 9001);
+ context1->setContextProperty("b", 9001);
- context2->setContextProperty("b", 15);
+ context2->setContextProperty("b", 42);
\endcode
While QML objects instantiated in a context are not strictly owned by that
@@ -212,7 +212,7 @@ bool QQmlContext::isValid() const
}
/*!
- Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the
+ Return the context's QQmlEngine, or \nullptr if the context has no QQmlEngine or the
QQmlEngine was destroyed.
*/
QQmlEngine *QQmlContext::engine() const
@@ -222,7 +222,7 @@ QQmlEngine *QQmlContext::engine() const
}
/*!
- Return the context's parent QQmlContext, or 0 if this context has no
+ Return the context's parent QQmlContext, or \nullptr if this context has no
parent or if the parent has been destroyed.
*/
QQmlContext *QQmlContext::parentContext() const
@@ -235,7 +235,7 @@ QQmlContext *QQmlContext::parentContext() const
}
/*!
- Return the context object, or 0 if there is no context object.
+ Return the context object, or \nullptr if there is no context object.
*/
QObject *QQmlContext::contextObject() const
{
diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h
index 82d15b8102..2da9541e5a 100644
--- a/src/qml/qml/qqmlcontext.h
+++ b/src/qml/qml/qqmlcontext.h
@@ -108,6 +108,4 @@ private:
};
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QList<QObject*>)
-
#endif // QQMLCONTEXT_H
diff --git a/src/qml/qml/qqmlcontextdata.cpp b/src/qml/qml/qqmlcontextdata.cpp
index f7decc2992..c07d52e2a3 100644
--- a/src/qml/qml/qqmlcontextdata.cpp
+++ b/src/qml/qml/qqmlcontextdata.cpp
@@ -173,7 +173,7 @@ QQmlContextData::~QQmlContextData()
QQmlGuardedContextData *contextGuard = m_contextGuards;
while (contextGuard) {
QQmlGuardedContextData *next = contextGuard->next();
- next->reset();
+ contextGuard->reset();
contextGuard = next;
}
m_contextGuards = nullptr;
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index b0df4f26dc..62bcaa41c6 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -102,8 +102,8 @@ void QQmlCustomParser::clearErrors()
void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
exceptions << error;
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index ff55ab49d4..27088e1032 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -258,7 +258,8 @@ public:
static void markAsDeleted(QObject *);
static void setQueuedForDeletion(QObject *);
- static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex);
+ static inline void flushPendingBinding(QObject *object, int coreIndex);
+ void flushPendingBinding(int coreIndex);
static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object)
{
@@ -279,8 +280,6 @@ private:
Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object);
- void flushPendingBindingImpl(QQmlPropertyIndex index);
-
Q_ALWAYS_INLINE bool hasBitSet(int bit) const
{
uint offset = offsetForBit(bit);
@@ -399,11 +398,11 @@ void QQmlData::clearPendingBindingBit(int coreIndex)
clearBit(coreIndex * 2 + 1);
}
-void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex)
+void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
{
- QQmlData *data = QQmlData::get(o, false);
- if (data && data->hasPendingBindingBit(propertyIndex.coreIndex()))
- data->flushPendingBindingImpl(propertyIndex);
+ QQmlData *data = QQmlData::get(object, false);
+ if (data && data->hasPendingBindingBit(coreIndex))
+ data->flushPendingBinding(coreIndex);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 4830558db4..ff8e2de238 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -162,7 +162,7 @@ QT_BEGIN_NAMESPACE
\endcode
*/
-bool QQmlEnginePrivate::qml_debugging_enabled = false;
+std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
bool QQmlEnginePrivate::s_designerMode = false;
bool QQmlEnginePrivate::designerMode()
@@ -379,138 +379,20 @@ The following functions are also on the Qt object.
The \c application object provides access to global application state
properties shared by many QML components.
- Its properties are:
-
- \table
- \row
- \li \c application.active
- \li
- Deprecated, use Qt.application.state == Qt.ApplicationActive instead.
-
- \row
- \li \c application.state
- \li
- This read-only property indicates the current state of the application.
-
- Possible values are:
-
- \list
- \li Qt.ApplicationActive - The application is the top-most and focused application, and the
- user is able to interact with the application.
- \li Qt.ApplicationInactive - The application is visible or partially visible, but not selected
- to be in front, the user cannot interact with the application.
- On desktop platforms, this typically means that the user activated
- another application. On mobile platforms, it is more common to
- enter this state when the OS is interrupting the user with for
- example incoming calls, SMS-messages or dialogs. This is usually a
- transient state during which the application is paused. The user
- may return focus to your application, but most of the time it will
- be the first indication that the application is going to be suspended.
- While in this state, consider pausing or stopping any activity that
- should not continue when the user cannot interact with your
- application, such as a video, a game, animations, or sensors.
- You should also avoid performing CPU-intensive tasks which might
- slow down the application in front.
- \li Qt.ApplicationSuspended - The application is suspended and not visible to the user. On
- mobile platforms, the application typically enters this state when
- the user returns to the home screen or switches to another
- application. While in this state, the application should ensure
- that the user perceives it as always alive and does not lose his
- progress, saving any persistent data. The application should cease
- all activities and be prepared for code execution to stop. While
- suspended, the application can be killed at any time without
- further warnings (for example when low memory forces the OS to purge
- suspended applications).
- \li Qt.ApplicationHidden - The application is hidden and runs in the background. This is the
- normal state for applications that need to do background processing,
- like playing music, while the user interacts with other applications.
- The application should free up all graphical resources when entering
- this state. A Qt Quick application should not usually handle this state
- at the QML level. Instead, you should unload the entire UI and reload
- the QML files whenever the application becomes active again.
- \endlist
-
- \row
- \li \c application.layoutDirection
- \li
- This read-only property can be used to query the default layout direction of the
- application. On system start-up, the default layout direction depends on the
- application's language. The property has a value of \c Qt.RightToLeft in locales
- where text and graphic elements are read from right to left, and \c Qt.LeftToRight
- where the reading direction flows from left to right. You can bind to this
- property to customize your application layouts to support both layout directions.
-
- Possible values are:
-
- \list
- \li Qt.LeftToRight - Text and graphics elements should be positioned
- from left to right.
- \li Qt.RightToLeft - Text and graphics elements should be positioned
- from right to left.
- \endlist
- \row
- \li \c application.font
- \li This read-only property holds the default application font as
- returned by \l QGuiApplication::font().
- \row
- \li \c application.arguments
- \li This is a string list of the arguments the executable was invoked with.
- \row
- \li \c application.name
- \li This is the application name set on the QCoreApplication instance. This property can be written
- to in order to set the application name.
- \row
- \li \c application.displayName (since Qt 5.9)
- \li This is the application display name set on the QGuiApplication instance. This property can be written
- to in order to set the application display name.
- \row
- \li \c application.version
- \li This is the application version set on the QCoreApplication instance. This property can be written
- to in order to set the application version.
- \row
- \li \c application.organization
- \li This is the organization name set on the QCoreApplication instance. This property can be written
- to in order to set the organization name.
- \row
- \li \c application.domain
- \li This is the organization domain set on the QCoreApplication instance. This property can be written
- to in order to set the organization domain.
-
- \row
- \li \c application.supportsMultipleWindows
- \li This read-only property can be used to determine whether or not the
- platform supports multiple windows. Some embedded platforms do not support
- multiple windows, for example.
-
- \row
- \li \c application.screens
- \li An array containing the descriptions of all connected screens. The
- elements of the array are objects with the same properties as the
- \l{Screen} attached object. In practice the array corresponds to the screen
- list returned by QGuiApplication::screens(). In addition to examining
- properties like name, width, height, etc., the array elements can also be
- assigned to the screen property of Window items, thus serving as an
- alternative to the C++ side's QWindow::setScreen(). This property has been
- added in Qt 5.9.
-
- \endtable
-
- The object also has one signal, aboutToQuit(), which is the same as \l QCoreApplication::aboutToQuit().
+ It is the same as the \l Application singleton.
The following example uses the \c application object to indicate
whether the application is currently active:
\snippet qml/application.qml document
- Note that when using QML without a QGuiApplication, the following properties will be undefined:
+ \note When using QML without a QGuiApplication, the following properties will be undefined:
\list
\li application.active
\li application.state
\li application.layoutDirection
\li application.font
\endlist
-
- \sa Screen, Window, {Window::screen}{Window.screen}
*/
/*!
@@ -807,17 +689,17 @@ void QQmlData::setQueuedForDeletion(QObject *object)
}
}
-void QQmlData::flushPendingBindingImpl(QQmlPropertyIndex index)
+void QQmlData::flushPendingBinding(int coreIndex)
{
- clearPendingBindingBit(index.coreIndex());
+ clearPendingBindingBit(coreIndex);
// Find the binding
QQmlAbstractBinding *b = bindings;
- while (b && (b->targetPropertyIndex().coreIndex() != index.coreIndex() ||
+ while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
b->targetPropertyIndex().hasValueTypeIndex()))
b = b->nextBinding();
- if (b && b->targetPropertyIndex().coreIndex() == index.coreIndex() &&
+ if (b && b->targetPropertyIndex().coreIndex() == coreIndex &&
!b->targetPropertyIndex().hasValueTypeIndex())
b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::DontRemoveBinding);
@@ -978,6 +860,16 @@ QQmlEngine::~QQmlEngine()
Once the component cache has been cleared, components must be loaded before
any new objects can be created.
+ \note Any existing objects created from QML components retain their types,
+ even if you clear the component cache. This includes singleton objects. If you
+ create more objects from the same QML code after clearing the cache, the new
+ objects will be of different types than the old ones. Assigning such a new
+ object to a property of its declared type belonging to an object created
+ before clearing the cache won't work.
+
+ As a general rule of thumb, make sure that no objects created from QML
+ components are alive when you clear the component cache.
+
\sa trimComponentCache()
*/
void QQmlEngine::clearComponentCache()
@@ -1368,21 +1260,12 @@ QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
QCoreApplication::installTranslator, to ensure that your user-interface
shows up-to-date translations.
- \note Due to a limitation in the implementation, this function
- refreshes all the engine's bindings, not only those that use strings
- marked for translation.
- This may be optimized in a future release.
-
\since 5.10
*/
void QQmlEngine::retranslate()
{
Q_D(QQmlEngine);
- for (QQmlRefPointer<QQmlContextData> context
- = QQmlContextData::get(d->rootContext)->childContexts();
- context; context = context->nextChild()) {
- context->refreshExpressions();
- }
+ d->translationLanguage.notify();
}
/*!
@@ -1541,7 +1424,7 @@ void QQmlData::deferData(
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
const QQmlPropertyData *property = propertyData.at(i);
- if (property && binding->flags & QV4::CompiledData::Binding::IsDeferredBinding)
+ if (property && binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
deferData->bindings.insert(property->coreIndex(), binding);
}
@@ -1886,7 +1769,7 @@ void QQmlEnginePrivate::cleanupScarceResources()
The newly added \a path will be first in the importPathList().
- \sa setImportPathList(), {QML Modules}
+ \sa setImportPathList(), {QML Modules}, {QML Import Path}
*/
void QQmlEngine::addImportPath(const QString& path)
{
@@ -1904,9 +1787,8 @@ void QQmlEngine::addImportPath(const QString& path)
provided by that module. A \c qmldir file is required for defining the
type version mapping and possibly QML extensions plugins.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -1920,9 +1802,11 @@ QStringList QQmlEngine::importPathList() const
Sets \a paths as the list of directories where the engine searches for
installed modules in a URL-based directory structure.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
+
+ \warning Calling setImportPathList does not preserve the default
+ import paths.
\sa importPathList(), addImportPath()
*/
@@ -2007,7 +1891,8 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList
Returns the directory where SQL and other offline
storage is placed.
- The SQL databases created with openDatabase() are stored here.
+ The SQL databases created with \c openDatabaseSync() are stored here.
+ \sa \l{Qt Quick Local Storage QML Types}
The default is QML/OfflineStorage in the platform-standard
user application data directory.
@@ -2406,4 +2291,6 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
QT_END_NAMESPACE
+#include "moc_qqmlengine_p.cpp"
+
#include "moc_qqmlengine.cpp"
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 499f9dd492..77ea35d034 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -75,6 +75,7 @@
#include <QtCore/qmutex.h>
#include <QtCore/qstring.h>
#include <QtCore/qthread.h>
+#include <QtCore/QMetaProperty>
#include <private/qobject_p.h>
@@ -83,6 +84,8 @@
#include <qproperty.h>
+#include <atomic>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -132,6 +135,8 @@ struct QPropertyChangeTrigger : QPropertyObserver {
QObject *target = nullptr;
int propertyIndex = 0;
static void trigger(QPropertyObserver *, QUntypedPropertyData *);
+
+ QMetaProperty property() const;
};
struct TriggerList : QPropertyChangeTrigger {
@@ -159,6 +164,7 @@ public:
QRecyclePool<TriggerList> qPropertyTriggerPool;
QQmlContext *rootContext;
+ Q_OBJECT_BINDABLE_PROPERTY(QQmlEnginePrivate, QString, translationLanguage);
#if !QT_CONFIG(qml_debug)
static const quintptr profiler = 0;
@@ -275,7 +281,7 @@ public:
static bool designerMode();
static void activateDesignerMode();
- static bool qml_debugging_enabled;
+ static std::atomic<bool> qml_debugging_enabled;
mutable QMutex networkAccessManagerMutex;
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index cb9096ae66..3cf2bce588 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -206,7 +206,7 @@ QQmlExpression::~QQmlExpression()
}
/*!
- Returns the QQmlEngine this expression is associated with, or 0 if there
+ Returns the QQmlEngine this expression is associated with, or \nullptr if there
is no association or the QQmlEngine has been destroyed.
*/
QQmlEngine *QQmlExpression::engine() const
@@ -216,7 +216,7 @@ QQmlEngine *QQmlExpression::engine() const
}
/*!
- Returns the QQmlContext this expression is associated with, or 0 if there
+ Returns the QQmlContext this expression is associated with, or \nullptr if there
is no association or the QQmlContext has been destroyed.
*/
QQmlContext *QQmlExpression::context() const
diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h
index 3df839a6a2..26c0cddb9e 100644
--- a/src/qml/qml/qqmlexpression_p.h
+++ b/src/qml/qml/qqmlexpression_p.h
@@ -76,6 +76,7 @@ public:
QVariant value(bool *isUndefined = nullptr);
QV4::ReturnedValue v4value(bool *isUndefined = nullptr);
+ bool mustCaptureBindableProperty() const final {return true;}
static inline QQmlExpressionPrivate *get(QQmlExpression *expr);
static inline QQmlExpression *get(QQmlExpressionPrivate *expr);
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 14704a0442..c2fc291d48 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -43,6 +43,21 @@
QT_BEGIN_NAMESPACE
/*!
+ \since 5.0
+ \inmodule QtQml
+ \class QQmlExtensionPlugin
+ \brief The QQmlExtensionPlugin class provides an abstract base for custom QML extension plugins
+ with custom type registration functions.
+
+ \ingroup plugins
+
+ \note If you need to write a plugin manually (which is rare) you should always use
+ \l{QQmlEngineExtensionPlugin}. QQmlExtensionPlugin only provides the registerTypes() and
+ unregisterTypes() functions in addition. You should not use them, but rather declare your
+ types with \l{QML_ELEMENT} and friends and have the build system take care of the registration.
+*/
+
+/*!
\since 5.14
\inmodule QtQml
\class QQmlEngineExtensionPlugin
@@ -61,7 +76,6 @@ QT_BEGIN_NAMESPACE
/*!
\fn void QQmlExtensionPlugin::registerTypes(const char *uri)
- \internal
Registers the QML types in the given \a uri. Subclasses should implement
this to call qmlRegisterType() for all types which are provided by the extension
@@ -127,9 +141,10 @@ void QQmlExtensionPlugin::unregisterTypes()
}
/*!
- \internal
-*/
-
+ Initializes the extension from the \a uri using the \a engine. Here an application
+ plugin might, for example, expose some data or objects to QML,
+ as context properties on the engine's root context.
+ */
void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_UNUSED(engine);
@@ -168,9 +183,12 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char
/*!
\macro Q_IMPORT_QML_PLUGIN(PluginName)
- \relates <QQmlExtensionPlugin>
+ \since 6.2
+ \relates QQmlEngineExtensionPlugin
+
+ Ensures the plugin whose metadata-declaring class is named \a PluginName
+ is linked into static builds.
- Ensures that a QML plugin works in static builds.
\sa Q_IMPORT_PLUGIN
*/
diff --git a/src/qml/qml/qqmlextensionplugin.h b/src/qml/qml/qqmlextensionplugin.h
index afd58e4032..f6fec10654 100644
--- a/src/qml/qml/qqmlextensionplugin.h
+++ b/src/qml/qml/qqmlextensionplugin.h
@@ -44,6 +44,12 @@
#include <QtCore/QUrl>
#include <QtQml/qqmlextensioninterface.h>
+#if defined(Q_CC_GHS)
+# define Q_GHS_KEEP_REFERENCE(S) QT_DO_PRAGMA(ghs reference S ##__Fv)
+#else
+# define Q_GHS_KEEP_REFERENCE(S)
+#endif
+
#define Q_IMPORT_QML_PLUGIN(PLUGIN) \
Q_IMPORT_PLUGIN(PLUGIN)
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index 4db8981975..dc2734d7d1 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -516,58 +516,91 @@ bool QQmlFile::isLocalFile(const QUrl &url)
{
QString scheme = url.scheme();
- if ((scheme.length() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) ||
- (scheme.length() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) {
+ // file: URLs with two slashes following the scheme can be interpreted as local files
+ // where the slashes are part of the path. Therefore, disregard the authority.
+ // See QUrl::toLocalFile().
+ if (scheme.length() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive))
return true;
+ if (scheme.length() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive))
+ return url.authority().isEmpty();
+
#if defined(Q_OS_ANDROID)
- } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) {
- return true;
+ if ((scheme.length() == 6
+ && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
+ || (scheme.length() == 7
+ && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))) {
+ return url.authority().isEmpty();
+ }
#endif
- } else {
+ return false;
+}
+
+static bool hasSchemeAndNoAuthority(const QString &url, const char *scheme, qsizetype schemeLength)
+{
+ const qsizetype urlLength = url.length();
+
+ if (urlLength < schemeLength + 1)
+ return false;
+
+ if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive))
+ return false;
+
+ if (url[schemeLength] != QLatin1Char(':'))
return false;
+
+ if (urlLength < schemeLength + 3)
+ return true;
+
+ const QLatin1Char slash('/');
+ if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
+ // Exactly two slashes denote an authority. We don't want that.
+ if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
+ return false;
}
+
+ return true;
}
/*!
Returns true if \a url is a local file that can be opened with QFile.
-Local file urls have either a qrc:/ or file:// scheme.
+Local file urls have either a qrc: or file: scheme.
-\note On Android, urls with assets:/ scheme are also considered local files.
+\note On Android, urls with assets: or content: scheme are also considered local files.
*/
bool QQmlFile::isLocalFile(const QString &url)
{
- if (url.length() < 5 /* qrc:/ */)
+ if (url.length() < 4 /* qrc: */)
return false;
- QChar f = url[0];
-
- if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
-
- return url.length() >= 7 /* file:// */ &&
- url.startsWith(QLatin1String(file_string), Qt::CaseInsensitive) &&
- url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
-
- } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
-
- return url.length() >= 5 /* qrc:/ */ &&
- url.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive) &&
- url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
-
+ switch (url[0].toLatin1()) {
+ case 'f':
+ case 'F': {
+ // file: URLs with two slashes following the scheme can be interpreted as local files
+ // where the slashes are part of the path. Therefore, disregard the authority.
+ // See QUrl::toLocalFile().
+ const qsizetype fileLength = strlen(file_string);
+ return url.startsWith(QLatin1String(file_string, file_string + fileLength),
+ Qt::CaseInsensitive)
+ && url.length() > fileLength
+ && url[fileLength] == QLatin1Char(':');
}
+ case 'q':
+ case 'Q':
+ return hasSchemeAndNoAuthority(url, qrc_string, strlen(qrc_string));
#if defined(Q_OS_ANDROID)
- else if (f == QLatin1Char('a') || f == QLatin1Char('A')) {
- return url.length() >= 8 /* assets:/ */ &&
- url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) &&
- url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/');
- } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) {
- return url.length() >= 9 /* content:/ */ &&
- url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) &&
- url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/');
- }
+ case 'a':
+ case 'A':
+ return hasSchemeAndNoAuthority(url, assets_string, strlen(assets_string));
+ case 'c':
+ case 'C':
+ return hasSchemeAndNoAuthority(url, content_string, strlen(content_string));
#endif
+ default:
+ break;
+ }
return false;
}
@@ -585,13 +618,10 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
}
#if defined(Q_OS_ANDROID)
- else if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0) {
- if (url.authority().isEmpty())
- return url.toString();
- return QString();
- } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) {
- return url.toString();
- }
+ if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0)
+ return url.authority().isEmpty() ? url.toString() : QString();
+ if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0)
+ return url.authority().isEmpty() ? url.toString() : QString();
#endif
return url.toLocalFile();
@@ -603,11 +633,28 @@ static QString toLocalFile(const QString &url)
if (!file.isLocalFile())
return QString();
- //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
+ // QUrl::toLocalFile() interprets two slashes as part of the path.
+ // Therefore windows hostnames like "//servername/path/to/file.txt" are preserved.
return file.toLocalFile();
}
+static bool isDoubleSlashed(const QString &url, qsizetype offset)
+{
+ const qsizetype urlLength = url.length();
+ if (urlLength < offset + 2)
+ return false;
+
+ const QLatin1Char slash('/');
+ if (url[offset] != slash || url[offset + 1] != slash)
+ return false;
+
+ if (urlLength < offset + 3)
+ return true;
+
+ return url[offset + 2] != slash;
+}
+
/*!
If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
empty string.
@@ -615,23 +662,28 @@ empty string.
QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
{
if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) {
- if (url.length() > 6)
- return QLatin1Char(':') + QStringView{url}.mid(6);
- return QString();
+ // Exactly two slashes are bad because that's a URL authority.
+ // One slash is fine and >= 3 slashes are file.
+ if (url.length() == 6 || url[6] != QLatin1Char('/')) {
+ Q_ASSERT(isDoubleSlashed(url, strlen("qrc:")));
+ return QString();
+ }
+ Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
+ return QLatin1Char(':') + QStringView{url}.mid(6);
}
if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
+ Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
if (url.length() > 4)
return QLatin1Char(':') + QStringView{url}.mid(4);
- return QString();
+ return QStringLiteral(":");
}
#if defined(Q_OS_ANDROID)
- else if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) {
- return url;
- } else if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive)) {
- return url;
- }
+ if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive))
+ return isDoubleSlashed(url, strlen("assets:")) ? QString() : url;
+ if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive))
+ return isDoubleSlashed(url, strlen("content:")) ? QString() : url;
#endif
return toLocalFile(url);
diff --git a/src/qml/qml/qqmlfileselector.cpp b/src/qml/qml/qqmlfileselector.cpp
index 4b773adc3e..5b1264bfd7 100644
--- a/src/qml/qml/qqmlfileselector.cpp
+++ b/src/qml/qml/qqmlfileselector.cpp
@@ -90,8 +90,7 @@ QT_BEGIN_NAMESPACE
directories used for selection must start with a '+' character, so you will not accidentally
trigger this feature unless you have directories with such names inside your project.
- If a new QQmlFileSelector is set on the engine, the old one will be replaced. Use
- \l QQmlFileSelector::get() to query or use the existing instance.
+ If a new QQmlFileSelector is set on the engine, the old one will be replaced.
*/
/*!
@@ -147,7 +146,7 @@ QQmlFileSelectorPrivate::~QQmlFileSelectorPrivate()
/*!
Sets the QFileSelector instance for use by the QQmlFileSelector to \a selector.
QQmlFileSelector does not take ownership of the new QFileSelector. To reset QQmlFileSelector
- to use its internal QFileSelector instance, call setSelector(0).
+ to use its internal QFileSelector instance, call setSelector(\nullptr).
*/
void QQmlFileSelector::setSelector(QFileSelector *selector)
@@ -180,7 +179,9 @@ void QQmlFileSelector::setExtraSelectors(const QStringList &strings)
#if QT_DEPRECATED_SINCE(6, 0)
/*!
- \deprecated
+ \deprecated [6.0] The file selector should not be accessed after it
+ is set. It may be in use. See below for further details.
+
Gets the QQmlFileSelector currently active on the target \a engine.
This method is deprecated. You should not retrieve the files selector from an
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index 3ac63926a0..85b2b898df 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -110,19 +110,19 @@ template <typename T>
class QQmlStrongJSQObjectReference : public QQmlGuard<T>
{
public:
- void setObject(T *o, QObject *parent) {
+ void setObject(T *obj, QObject *parent) {
T *old = this->object();
- if (o == old)
+ if (obj == old)
return;
if (m_jsOwnership && old && old->parent() == parent)
QQml_setParent_noEvent(old, nullptr);
- this->QQmlGuard<T>::operator=(o);
+ this->QQmlGuard<T>::operator=(obj);
- if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) {
+ if (obj && !obj->parent() && !QQmlData::keepAliveDuringGarbageCollection(obj)) {
m_jsOwnership = true;
- QQml_setParent_noEvent(o, parent);
+ QQml_setParent_noEvent(obj, parent);
} else {
m_jsOwnership = false;
}
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index a5c11fa2f2..d04d6751cd 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -46,7 +46,7 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qpluginloader.h>
#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qreadwritelock.h>
+#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlextensioninterface.h>
#include <QtQml/qqmlextensionplugin.h>
#include <private/qqmlextensionplugin_p.h>
@@ -1169,6 +1169,26 @@ QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDataba
return stableRelativePath;
}
+/* removes all file selector occurrences in path
+ firstPlus is the position of the initial '+' in the path
+ which we always have as we check for '+' to decide whether
+ we need to do some work at all
+*/
+static QString pathWithoutFileSelectors(QString path, // we want a copy of path
+ qsizetype firstPlus)
+{
+ do {
+ Q_ASSERT(path.at(firstPlus) == u'+');
+ const auto eos = path.size();
+ qsizetype terminatingSlashPos = firstPlus + 1;
+ while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
+ ++terminatingSlashPos;
+ path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
+ firstPlus = path.indexOf(u'+', firstPlus);
+ } while (firstPlus != -1);
+ return path;
+}
+
/*!
\internal
@@ -1215,10 +1235,42 @@ QTypeRevision QQmlImportsPrivate::matchingQmldirVersion(
typedef QQmlDirComponents::const_iterator ConstIterator;
const QQmlDirComponents &components = qmldir.components();
+ QMultiHash<QString, ConstIterator> baseFileName2ConflictingComponents;
+
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
+ // ugly heuristic to deal with file selectors
+ const auto comp2PotentialFileSelectorPos = cit2->fileName.indexOf(u'+');
+ const bool comp2MightHaveFileSelector = comp2PotentialFileSelectorPos != -1;
+ /* If we detect conflicting paths, we check if they agree when we remove anything looking like a
+ file selector.
+ We need to create copies of the filenames, otherwise QString::replace would modify the
+ existing file-names
+ */
+ QString compFileName1 = cit->fileName;
+ QString compFileName2 = cit2->fileName;
+ if (auto fileSelectorPos1 = compFileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
+ // existing entry was file selector entry, fix it up
+ // it could also be the case that _both_ are using file selectors
+ QString baseName = comp2MightHaveFileSelector ? pathWithoutFileSelectors(compFileName2,
+ comp2PotentialFileSelectorPos)
+ : compFileName2;
+ if (pathWithoutFileSelectors(compFileName1, fileSelectorPos1) == baseName) {
+ baseFileName2ConflictingComponents.insert(baseName, cit);
+ baseFileName2ConflictingComponents.insert(baseName, cit2);
+ continue;
+ }
+ // fall through to error case
+ } else if (comp2MightHaveFileSelector) {
+ // new entry contains file selector (and we now that cit did not)
+ if (pathWithoutFileSelectors(compFileName2, comp2PotentialFileSelectorPos) == compFileName1) {
+ baseFileName2ConflictingComponents.insert(compFileName1, cit2);
+ continue;
+ }
+ // fall through to error case
+ }
// This entry clashes with a predecessor
QQmlError error;
error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
@@ -1232,6 +1284,14 @@ QTypeRevision QQmlImportsPrivate::matchingQmldirVersion(
addVersion(cit->version);
}
+ // ensure that all components point to the actual base URL, and let the file selectors resolve them correctly during URL resolution
+ for (auto keyIt = baseFileName2ConflictingComponents.keyBegin(); keyIt != baseFileName2ConflictingComponents.keyEnd(); ++keyIt) {
+ const QString& baseFileName = *keyIt;
+ const auto conflictingComponents = baseFileName2ConflictingComponents.values(baseFileName);
+ for (ConstIterator component: conflictingComponents)
+ component->fileName = baseFileName;
+ }
+
typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
const QQmlDirScripts &scripts = qmldir.scripts();
@@ -1369,8 +1429,12 @@ QTypeRevision QQmlImportsPrivate::addLibraryImport(
return matchingVersion;
if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) {
- if (qmldir.plugins().isEmpty() && !qmldir.imports().isEmpty())
- return validVersion(); // This is a pure redirection
+ if (qmldir.plugins().isEmpty()) {
+ if (!qmldir.imports().isEmpty())
+ return validVersion(); // This is a pure redirection
+ if (qmldir.hasTypeInfo())
+ return validVersion(); // A pure C++ module without plugin
+ }
errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version)));
return QTypeRevision();
} else if (qmldir.hasContent()) {
@@ -1388,6 +1452,18 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
const QString& uri, const QString &prefix, QTypeRevision version, uint flags,
QQmlImportDatabase *database, QList<QQmlError> *errors)
{
+ if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
+ QQmlError error;
+ const QString fix = uri.startsWith(Slash) ? QLatin1String("file:") + uri
+ : QLatin1String("qrc") + uri;
+ error.setDescription(QQmlImportDatabase::tr(
+ "\"%1\" is not a valid import URL. "
+ "You can pass relative paths or URLs with schema, but not "
+ "absolute paths or resource paths. Try \"%2\".").arg(uri, fix));
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
Q_ASSERT(errors);
QQmlImportNamespace *nameSpace = importNamespace(prefix);
@@ -1616,7 +1692,7 @@ QTypeRevision QQmlImports::addLibraryImport(
qCDebug(lcQmlImport)
<< "addLibraryImport:" << qPrintable(baseUrl().toString())
- << uri << version << "as" << prefix;
+ << uri << "version '" << version << "'" << "as" << prefix;
return d->addLibraryImport(uri, prefix, version, qmldirIdentifier, qmldirUrl, flags,
importDb, errors);
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 2d27fed648..ba0b6286fd 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -42,6 +42,7 @@
#include <QtCore/qurl.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qset.h>
#include <QtCore/qstringlist.h>
#include <QtQml/qqmlengine.h>
@@ -73,6 +74,8 @@ class QQmlImportDatabase;
class QQmlTypeLoader;
class QQmlTypeLoaderQmldirContent;
+const QLoggingCategory &lcQmlImport();
+
namespace QQmlImport {
enum RecursionRestriction { PreventRecursion, AllowRecursion };
}
@@ -217,6 +220,11 @@ class Q_QML_PRIVATE_EXPORT QQmlImportDatabase
public:
enum PathType { Local, Remote, LocalOrRemote };
+ enum LocalQmldirSearchLocation {
+ QmldirFileAndCache,
+ QmldirCacheOnly,
+ };
+
enum LocalQmldirResult {
QmldirFound,
QmldirNotFound,
@@ -240,7 +248,8 @@ public:
template<typename Callback>
LocalQmldirResult locateLocalQmldir(
- const QString &uri, QTypeRevision version, const Callback &callback);
+ const QString &uri, QTypeRevision version, LocalQmldirSearchLocation location,
+ const Callback &callback);
static QTypeRevision lockModule(const QString &uri, const QString &typeNamespace,
QTypeRevision version, QList<QQmlError> *errors);
@@ -273,7 +282,8 @@ private:
template<typename Callback>
QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
- const QString &uri, QTypeRevision version, const Callback &callback)
+ const QString &uri, QTypeRevision version,
+ QQmlImportDatabase::LocalQmldirSearchLocation location, const Callback &callback)
{
// Check cache first
@@ -301,7 +311,7 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
// Do not try to construct the cache if it already had any entries for the URI.
// Otherwise we might duplicate cache entries.
- if (result != QmldirNotFound)
+ if (location == QmldirCacheOnly || result != QmldirNotFound)
return result;
const bool hasInterceptors = !engine->urlInterceptors().isEmpty();
@@ -313,6 +323,7 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
uri, localImportPaths, version);
+ QString qmldirAbsoluteFilePath;
for (QString qmldirPath : qmlDirPaths) {
if (hasInterceptors) {
const QUrl intercepted = engine->interceptUrl(
@@ -326,7 +337,7 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
}
}
- QString qmldirAbsoluteFilePath = absoluteFilePath(qmldirPath);
+ qmldirAbsoluteFilePath = absoluteFilePath(qmldirPath);
if (!qmldirAbsoluteFilePath.isEmpty()) {
QString url;
const QString absolutePath = qmldirAbsoluteFilePath.left(
@@ -373,6 +384,15 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
cache->qmldirPathUrl = QStringLiteral("intercepted");
}
qmldirCache.insert(uri, cache);
+
+ if (result == QmldirNotFound) {
+ qCDebug(lcQmlImport)
+ << "locateLocalQmldir:" << qPrintable(uri) << "module's qmldir file not found";
+ }
+ } else {
+ qCDebug(lcQmlImport)
+ << "locateLocalQmldir:" << qPrintable(uri) << "module's qmldir found at"
+ << qmldirAbsoluteFilePath;
}
return result;
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 7d4ffcec3f..59f4bf296e 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -269,6 +269,7 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i)
}
}
+
void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
{
if (!compilationUnit)
@@ -280,6 +281,20 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
// get a copy of the engine pointer as it might get reset;
QQmlEnginePrivate *enginePriv = this->enginePriv;
+ // Incubating objects takes quite a bit more stack space than our usual V4 function
+ enum { EstimatedSizeInV4Frames = 2 };
+ QV4::ExecutionEngineCallDepthRecorder<EstimatedSizeInV4Frames> callDepthRecorder(
+ compilationUnit->engine);
+ if (callDepthRecorder.hasOverflow()) {
+ QQmlError error;
+ error.setMessageType(QtCriticalMsg);
+ error.setUrl(compilationUnit->url());
+ error.setDescription(QQmlComponent::tr("Maximum call stack size exceeded."));
+ errors << error;
+ progress = QQmlIncubatorPrivate::Completed;
+ goto finishIncubate;
+ }
+
if (!vmeGuard.isOK()) {
QQmlError error;
error.setMessageType(QtInfoMsg);
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index ee75a2b4f7..c2d9aa941c 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -101,10 +101,11 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
serializedObject->location);
object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
- object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
- object->isInlineComponent = serializedObject->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
- object->flags = serializedObject->flags;
- object->id = serializedObject->id;
+ object->defaultPropertyIsAlias = serializedObject->hasAliasAsDefaultProperty();
+ object->isInlineComponent = serializedObject->hasFlag(
+ QV4::CompiledData::Object::IsInlineComponentRoot);
+ object->flags = serializedObject->flags();
+ object->id = serializedObject->objectId();
object->locationOfIdProperty = serializedObject->locationOfIdProperty;
QVector<int> functionIndices;
@@ -114,7 +115,7 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
QmlIR::Binding *b = pool->New<QmlIR::Binding>();
*static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i];
object->bindings->append(b);
- if (b->type == QV4::CompiledData::Binding::Type_Script) {
+ if (b->type() == QV4::CompiledData::Binding::Type_Script) {
functionIndices.append(b->value.compiledScriptIndex);
b->value.compiledScriptIndex = functionIndices.count() - 1;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 75bc944fee..f937a3fff8 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -129,8 +129,8 @@ QString QQmlJavaScriptExpression::expressionIdentifier() const
{
if (auto f = function()) {
QString url = f->sourceFile();
- uint lineNumber = f->compiledFunction->location.line;
- uint columnNumber = f->compiledFunction->location.column;
+ uint lineNumber = f->compiledFunction->location.line();
+ uint columnNumber = f->compiledFunction->location.column();
return url + QString::asprintf(":%u:%u", lineNumber, columnNumber);
}
@@ -221,9 +221,6 @@ public:
while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst())
g->Delete();
- if (!watcher.wasDeleted())
- capture.expression->setTranslationsCaptured(capture.translationCaptured);
-
ep->propertyCapture = lastPropertyCapture;
}
@@ -393,12 +390,27 @@ void QQmlPropertyCapture::captureProperty(
captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
}
+void QQmlPropertyCapture::captureTranslation()
+{
+ // use a unique invalid index to avoid needlessly querying the metaobject for
+ // the correct index of of the translationLanguage property
+ int const invalidIndex = -2;
+ for (auto trigger = expression->qpropertyChangeTriggers; trigger;
+ trigger = trigger->next) {
+ if (trigger->target == engine && trigger->propertyIndex == invalidIndex)
+ return; // already installed
+ }
+ auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
+
+ trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
+}
+
void QQmlPropertyCapture::captureBindableProperty(
QObject *o, const QMetaObject *metaObjectForBindable, int c)
{
// if the property is a QPropery, and we're binding to a QProperty
// the automatic capturing process already takes care of everything
- if (typeid(QQmlPropertyBindingJS) == typeid(*expression))
+ if (!expression->mustCaptureBindableProperty())
return;
for (auto trigger = expression->qpropertyChangeTriggers; trigger;
trigger = trigger->next) {
@@ -541,6 +553,16 @@ void QPropertyChangeTrigger::trigger(QPropertyObserver *observer, QUntypedProper
This->m_expression->expressionChanged();
}
+QMetaProperty QPropertyChangeTrigger::property() const
+{
+ if (!target)
+ return {};
+ auto const mo = target->metaObject();
+ if (!mo)
+ return {};
+ return mo->property(propertyIndex);
+}
+
QPropertyChangeTrigger *QQmlJavaScriptExpression::allocatePropertyChangeTrigger(QObject *target, int propertyIndex)
{
auto trigger = QQmlEnginePrivate::get(engine())->qPropertyTriggerPool.New( this );
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 7a741afb75..bfdc922729 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -158,6 +158,7 @@ public:
void clearError();
void clearActiveGuards();
QQmlDelayedError *delayedError();
+ virtual bool mustCaptureBindableProperty() const {return true;}
static QV4::ReturnedValue evalFunction(
const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope, const QString &code,
@@ -186,22 +187,12 @@ protected:
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next, GuardTag> activeGuards;
- void setTranslationsCaptured(bool captured) {
- Tag newTag = captured ? TranslationsCaptured : NoTag;
- if (m_error.tag() & InEvaluationLoop)
- newTag = Tag(newTag | InEvaluationLoop);
- m_error.setTag(newTag);
- }
- bool translationsCaptured() const { return m_error.tag() & TranslationsCaptured; }
-
enum Tag {
NoTag,
- TranslationsCaptured,
InEvaluationLoop
};
- // m_error:flag1 translationsCapturedDuringEvaluation
- QTaggedPointer<QQmlDelayedError> m_error;
+ QTaggedPointer<QQmlDelayedError, Tag> m_error;
private:
friend class QQmlContextData;
@@ -238,14 +229,13 @@ public:
void captureProperty(QQmlNotifier *);
void captureProperty(QObject *, int, int, bool doNotify = true);
void captureProperty(QObject *, const QQmlPropertyCache *, const QQmlPropertyData *, bool doNotify = true);
- void captureTranslation() { translationCaptured = true; }
+ void captureTranslation();
QQmlEngine *engine;
QQmlJavaScriptExpression *expression;
QQmlJavaScriptExpression::DeleteWatcher *watcher;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards;
QStringList *errorString;
- bool translationCaptured = false;
private:
void captureBindableProperty(QObject *o, const QMetaObject *metaObjectForBindable, int c);
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 3589dce3f0..1794a27a9d 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -703,7 +703,7 @@ ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const QV4::FunctionObject
LOCALE_STRING_PROPERTY(name)
LOCALE_STRING_PROPERTY(nativeLanguageName)
-LOCALE_STRING_PROPERTY(nativeCountryName)
+QT_IGNORE_DEPRECATIONS(LOCALE_STRING_PROPERTY(nativeCountryName))
LOCALE_STRING_PROPERTY(decimalPoint)
LOCALE_STRING_PROPERTY(groupSeparator)
LOCALE_STRING_PROPERTY(percent)
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
index b59a26e17e..98dfe62f0c 100644
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -129,7 +129,7 @@ void QQmlLoggingCategory::componentComplete()
{
m_initialized = true;
if (m_name.isNull()) {
- qmlWarning(this) << QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !");
+ qmlWarning(this) << QLatin1String("Declaring the name of a LoggingCategory is mandatory and cannot be changed later");
} else {
QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData(), QtMsgType(m_defaultLogLevel)));
m_category.swap(category);
@@ -138,23 +138,30 @@ void QQmlLoggingCategory::componentComplete()
void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel)
{
+ if (m_defaultLogLevel == defaultLogLevel)
+ return;
+
if (m_initialized) {
- qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created");
+ qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed");
return;
}
m_defaultLogLevel = defaultLogLevel;
}
-
void QQmlLoggingCategory::setName(const QString &name)
{
+ const QByteArray newName = name.toUtf8();
+
+ if (m_name == newName)
+ return;
+
if (m_initialized) {
- qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the Item is created");
+ qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the component is completed");
return;
}
- m_name = name.toUtf8();
+ m_name = newName;
}
#include "moc_qqmlloggingcategory_p.cpp"
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index e412263825..fdf74f4873 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -47,10 +47,6 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qv4executablecompilationunit_p.h>
-#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
-#endif
-
#include <QtCore/qcoreapplication.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
@@ -230,7 +226,8 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
}
void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
- const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd)
+ const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd,
+ QQmlMetaType::ClonePolicy policy)
{
// Set classname
builder.setClassName(ignoreEnd->className());
@@ -247,41 +244,42 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_PROPERTY
- for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
- QMetaProperty property = mo->property(ii);
+ if (policy != QQmlMetaType::CloneEnumsOnly) {
+ // Clone Q_METHODS - do this first to avoid duplicating the notify signals.
+ for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) {
+ QMetaMethod method = mo->method(ii);
- int otherIndex = ignoreEnd->indexOfProperty(property.name());
- if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
- builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void"));
- // Skip
- } else {
- builder.addProperty(property);
- }
- }
+ // More complex - need to search name
+ QByteArray name = method.name();
- // Clone Q_METHODS
- for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) {
- QMetaMethod method = mo->method(ii);
+ bool found = false;
- // More complex - need to search name
- QByteArray name = method.name();
+ for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount();
+ !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) {
+ QMetaMethod other = ignoreEnd->method(ii);
- bool found = false;
+ found = name == other.name();
+ }
- for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount();
- !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount();
- ++ii) {
+ QMetaMethodBuilder m = builder.addMethod(method);
+ if (found) // SKIP
+ m.setAccess(QMetaMethod::Private);
+ }
- QMetaMethod other = ignoreEnd->method(ii);
+ // Clone Q_PROPERTY
+ for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
+ QMetaProperty property = mo->property(ii);
- found = name == other.name();
+ int otherIndex = ignoreEnd->indexOfProperty(property.name());
+ if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
+ builder.addProperty(QByteArray("__qml_ignore__") + property.name(),
+ QByteArray("void"));
+ // Skip
+ } else {
+ builder.addProperty(property);
+ }
}
-
- QMetaMethodBuilder m = builder.addMethod(method);
- if (found) // SKIP
- m.setAccess(QMetaMethod::Private);
}
// Clone Q_ENUMS
@@ -339,6 +337,10 @@ void QQmlMetaType::clearTypeRegistrations()
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->undeletableTypes.clear();
+
+ for (auto it = data->propertyCaches.begin(), end = data->propertyCaches.end(); it != end; ++it)
+ (*it)->release();
+ data->propertyCaches.clear();
}
int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function)
@@ -431,7 +433,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (uri && !typeName.isEmpty()) {
QString nameSpace = QString::fromUtf8(uri);
QQmlTypeModule *qqtm = data->findTypeModule(nameSpace, version);
- if (qqtm && qqtm->isLocked()) {
+ if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) {
QString failure(QCoreApplication::translate(
"qmlRegisterType",
"Cannot install %1 '%2' into protected module '%3' version '%4'"));
@@ -588,12 +590,14 @@ void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &t
QMetaType metaType(typeIds.id);
QMetaType listMetaType(typeIds.listId);
+ // This may be called from delayed dtors on shutdown when the data is already gone.
QQmlMetaTypeDataPtr data;
-
- if (QQmlValueType *vt = data->metaTypeToValueType.take(metaType.id()))
- delete vt;
- if (QQmlValueType *vt = data->metaTypeToValueType.take(listMetaType.id()))
- delete vt;
+ if (data.isValid()) {
+ if (QQmlValueType *vt = data->metaTypeToValueType.take(metaType.id()))
+ delete vt;
+ if (QQmlValueType *vt = data->metaTypeToValueType.take(listMetaType.id()))
+ delete vt;
+ }
QMetaType::unregisterMetaType(metaType);
QMetaType::unregisterMetaType(listMetaType);
@@ -645,18 +649,19 @@ void QQmlMetaType::unregisterSequentialContainer(int id)
unregisterType(id);
}
-bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, bool protectAllVersions)
+bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version,
+ bool weakProtectAllVersions)
{
QQmlMetaTypeDataPtr data;
if (version.hasMajorVersion()) {
- if (QQmlTypeModule *module = data->findTypeModule(uri, version)) {
- if (!protectAllVersions) {
- module->lock();
- return true;
+ if (QQmlTypeModule *module = data->findTypeModule(uri, version)) {
+ if (!weakProtectAllVersions) {
+ module->setLockLevel(QQmlTypeModule::LockLevel::Strong);
+ return true;
+ }
+ } else {
+ return false;
}
- } else {
- return false;
- }
}
const auto range = std::equal_range(
@@ -664,7 +669,7 @@ bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, bool
std::less<ModuleUri>());
for (auto it = range.first; it != range.second; ++it)
- (*it)->lock();
+ (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak);
return range.first != range.second;
}
@@ -981,12 +986,12 @@ QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri)
/*
Returns true if a module \a uri of this version is installed and locked;
*/
-bool QQmlMetaType::isLockedModule(const QString &uri, QTypeRevision version)
+bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version)
{
QQmlMetaTypeDataPtr data;
if (QQmlTypeModule* qqtm = data->findTypeModule(uri, version))
- return qqtm->isLocked();
+ return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong;
return false;
}
@@ -1522,7 +1527,8 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject
return;
QMetaObjectBuilder builder;
- clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject);
+ clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject,
+ extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly);
builder.setFlags(MetaObjectFlag::DynamicMetaObject);
QMetaObject *mmo = builder.toMetaObject();
mmo->d.superdata = baseMetaObject;
@@ -1598,17 +1604,7 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
case QMetaType::QEasingCurve:
return &QQmlEasingValueType::staticMetaObject;
#endif
-#if QT_CONFIG(qml_itemmodel)
- case QMetaType::QModelIndex:
- return &QQmlModelIndexValueType::staticMetaObject;
- case QMetaType::QPersistentModelIndex:
- return &QQmlPersistentModelIndexValueType::staticMetaObject;
-#endif
default:
-#if QT_CONFIG(qml_itemmodel)
- if (metaType == QMetaType::fromType<QItemSelectionRange>())
- return &QQmlItemSelectionRangeValueType::staticMetaObject;
-#endif
break;
}
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 7ee16f9dc9..dc08263511 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -147,7 +147,7 @@ public:
static void registerModule(const char *uri, QTypeRevision version);
static bool protectModule(const QString &uri, QTypeRevision version,
- bool protectAllVersions = false);
+ bool weakProtectAllVersions = false);
static void registerModuleImport(const QString &uri, QTypeRevision version,
const QQmlDirParser::Import &import);
@@ -197,7 +197,7 @@ public:
static bool isList(QMetaType type);
static QTypeRevision latestModuleVersion(const QString &uri);
- static bool isLockedModule(const QString &uri, QTypeRevision version);
+ static bool isStronglyLockedModule(const QString &uri, QTypeRevision version);
static QTypeRevision matchingModuleVersion(const QString &module, QTypeRevision version);
static QQmlTypeModule *typeModule(const QString &uri, QTypeRevision version);
@@ -245,8 +245,13 @@ public:
const QMetaObject *baseMetaObject,
QMetaObject *lastMetaObject);
+ enum ClonePolicy {
+ CloneAll, // default
+ CloneEnumsOnly, // skip properties and methods
+ };
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
- const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd);
+ const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd,
+ ClonePolicy policy);
static void qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)());
static void qmlRemoveModuleRegistration(const QString &uri);
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index 71901e23fe..6b21e23349 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -145,8 +145,9 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache(const QMetaObj
return rv;
if (!metaObject->superClass()) {
- QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject);
- propertyCaches.insert(metaObject, rv);
+ QQmlRefPointer<QQmlPropertyCache> rv = QQmlPropertyCache::createStandalone(metaObject);
+ rv->addref(); // extra ref for the propertyCaches storage
+ propertyCaches.insert(metaObject, rv.data());
return rv;
}
auto super = propertyCache(metaObject->superClass(), version);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index c0cc917499..0a96bb0af8 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -168,13 +168,16 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
phase = CreatingObjects;
int objectToCreate;
+ bool isComponentRoot = false; // either a "real" component of or an inline component
if (subComponentIndex == -1) {
objectToCreate = /*root object*/0;
+ isComponentRoot = true;
} else {
Q_ASSERT(subComponentIndex >= 0);
if (flags & CreationFlags::InlineComponent) {
objectToCreate = subComponentIndex;
+ isComponentRoot = true;
} else {
Q_ASSERT(flags & CreationFlags::NormalObject);
const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
@@ -199,7 +202,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
if (topLevelCreator)
sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
- if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) {
+ if (isComponentRoot && compilationUnit->dependentScripts.count()) {
QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count()));
context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue()));
QV4::ScopedValue v(scope);
@@ -290,7 +293,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
if (binding) {
Q_ASSERT(qmlProperty);
- Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding);
+ Q_ASSERT(binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding));
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
@@ -352,7 +355,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
int propertyType = property->propType().id();
if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) {
propertyType = QMetaType::Int;
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
@@ -366,18 +369,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
auto assertOrNull = [&](bool ok)
{
- Q_ASSERT(ok || binding->type == QV4::CompiledData::Binding::Type_Null);
+ Q_ASSERT(ok || binding->type() == QV4::CompiledData::Binding::Type_Null);
Q_UNUSED(ok);
};
- auto assertType = [&](QV4::CompiledData::Binding::ValueType type)
+ auto assertType = [&](QV4::CompiledData::Binding::Type type)
{
- Q_ASSERT(binding->type == type || binding->type == QV4::CompiledData::Binding::Type_Null);
+ Q_ASSERT(binding->type()== type || binding->type() == QV4::CompiledData::Binding::Type_Null);
Q_UNUSED(type);
};
if (property->isQObject()) {
- if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
QObject *value = nullptr;
const bool ok = property->writeProperty(_qobject, &value, propertyWriteFlags);
Q_ASSERT(ok);
@@ -388,7 +391,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
switch (propertyType) {
case QMetaType::QVariant: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Number) {
double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) == n) {
if (property->isVarProperty()) {
@@ -406,14 +409,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
}
- } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+ } else if (binding->type() == QV4::CompiledData::Binding::Type_Boolean) {
if (property->isVarProperty()) {
_vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromBoolean(binding->valueAsBoolean()));
} else {
QVariant value(binding->valueAsBoolean());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
- } else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ } else if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
if (property->isVarProperty()) {
_vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::nullValue());
} else {
@@ -453,7 +456,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
case QMetaType::QUrl: {
assertType(QV4::CompiledData::Binding::Type_String);
const QString string = compilationUnit->bindingValueAsString(binding);
- QUrl value(string);
+ QUrl value = (!string.isEmpty() && QQmlPropertyPrivate::resolveUrlsOnAssignment())
+ ? compilationUnit->finalUrl().resolved(QUrl(string))
+ : QUrl(string);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
@@ -600,7 +605,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
} else if (propertyType == qMetaTypeId<QList<QUrl> >()) {
assertType(QV4::CompiledData::Binding::Type_String);
- QList<QUrl> value { QUrl(compilationUnit->bindingValueAsString(binding)) };
+ const QUrl url(compilationUnit->bindingValueAsString(binding));
+ QList<QUrl> value {
+ QQmlPropertyPrivate::resolveUrlsOnAssignment()
+ ? compilationUnit->finalUrl().resolved(url)
+ : url
+ };
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
} else if (propertyType == qMetaTypeId<QList<QString> >()) {
@@ -611,18 +621,24 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
} else if (propertyType == qMetaTypeId<QJSValue>()) {
QJSValue value;
- if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Boolean:
value = QJSValue(binding->valueAsBoolean());
- } else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = compilationUnit->bindingValueAsNumber(binding);
- if (double(int(n)) == n) {
+ break;
+ case QV4::CompiledData::Binding::Type_Number: {
+ const double n = compilationUnit->bindingValueAsNumber(binding);
+ if (double(int(n)) == n)
value = QJSValue(int(n));
- } else
+ else
value = QJSValue(n);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ break;
+ }
+ case QV4::CompiledData::Binding::Type_Null:
value = QJSValue::NullValue;
- } else {
+ break;
+ default:
value = QJSValue(compilationUnit->bindingValueAsString(binding));
+ break;
}
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -662,8 +678,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType().id() == QMetaType::QString) {
QV4::CompiledData::Binding idBinding;
idBinding.propertyNameIndex = 0; // Not used
- idBinding.flags = 0;
- idBinding.type = QV4::CompiledData::Binding::Type_String;
+ idBinding.clearFlags();
+ idBinding.setType(QV4::CompiledData::Binding::Type_String);
idBinding.stringIndex = _compiledObject->idNameIndex;
idBinding.location = _compiledObject->location; // ###
setPropertyValue(idProperty, &idBinding);
@@ -718,10 +734,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
}
- if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)) {
if (!applyDeferredBindings)
continue;
} else {
@@ -778,7 +794,8 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding)
{
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
Q_ASSERT(tr);
@@ -793,7 +810,15 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
QObject *qmlObject = qmlAttachedPropertiesObject(
_qobject, attachedType.attachedPropertiesFunction(QQmlEnginePrivate::get(engine)));
- if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject, /*value type property*/nullptr))
+ if (!qmlObject) {
+ recordError(binding->location,
+ QStringLiteral("Could not create attached properties object '%1'")
+ .arg(QString::fromUtf8(attachedType.typeName())));
+ return false;
+ }
+
+ if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject,
+ /*value type property*/ nullptr, binding))
return false;
return true;
}
@@ -802,11 +827,11 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
if (bindingProperty && bindingProperty->propType() == QMetaType::fromType<QQmlScriptString>()) {
QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding),
context->asQQmlContext(), _scopeObject);
- ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
- ss.d.data()->lineNumber = binding->location.line;
- ss.d.data()->columnNumber = binding->location.column;
- ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String;
- ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number;
+ ss.d.data()->bindingId = bindingType == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
+ ss.d.data()->lineNumber = binding->location.line();
+ ss.d.data()->columnNumber = binding->location.column();
+ ss.d.data()->isStringLiteral = bindingType == QV4::CompiledData::Binding::Type_String;
+ ss.d.data()->isNumberLiteral = bindingType == QV4::CompiledData::Binding::Type_Number;
ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding);
QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
@@ -818,7 +843,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
QObject *createdSubObject = nullptr;
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget);
if (!createdSubObject)
return false;
@@ -827,7 +852,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
if (!bindingProperty) // ### error
return true;
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ if (bindingType == QV4::CompiledData::Binding::Type_GroupProperty) {
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex);
if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) {
@@ -858,7 +883,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
bindingTarget = groupObject;
}
- if (!populateInstance(binding->value.objectIndex, groupObject, bindingTarget, valueTypeProperty))
+ if (!populateInstance(binding->value.objectIndex, groupObject, bindingTarget,
+ valueTypeProperty, binding))
return false;
if (valueType)
@@ -868,14 +894,26 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
}
- if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
- && !_valueTypeProperty)
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ const bool allowedToRemoveBinding
+ = !(bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
+ && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment)
+ && !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
+ && !_valueTypeProperty;
+
+ if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && allowedToRemoveBinding) {
QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
+ } else if (bindingProperty->isBindable() && allowedToRemoveBinding) {
+ QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
+ auto it = std::remove_if(pendingBindings.begin(), pendingBindings.end(), [&](const DeferredQPropertyBinding &deferred) {
+ return deferred.properyIndex == bindingProperty->coreIndex() && deferred.target == _bindingTarget;
+ });
+ pendingBindings.erase(it, pendingBindings.end());
+ }
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsPropertyObserver) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver) {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex());
QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(
@@ -915,7 +953,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
QQmlPropertyIndex index(bindingProperty->coreIndex(), -1);
qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index);
}
- sharedState.data()->allQPropertyBindings.emplaceBack(_bindingTarget, bindingProperty->coreIndex(), qmlBinding);
+ sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding });
} else {
// When writing bindings to grouped properties implemented as value types,
// such as point.x: { someExpression; }, then the binding is installed on
@@ -966,8 +1004,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
return true;
}
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) {
// ### determine value source and interceptor casts ahead of time.
QQmlType type = qmlTypeForObject(createdSubObject);
Q_ASSERT(type.isValid());
@@ -1031,7 +1069,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
}
// Assigning object to signal property? ### Qt 7: Remove that functionality
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
if (!bindingProperty->isFunction()) {
recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject)));
return false;
@@ -1156,16 +1194,16 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location,
{
QQmlError error;
error.setUrl(compilationUnit->url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
errors << error;
}
void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const
{
- if (object->id >= 0)
- context->setIdValue(object->id, instance);
+ if (object->objectId() >= 0)
+ context->setIdValue(object->objectId(), instance);
}
QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject)
@@ -1185,7 +1223,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QQmlParserStatus *parserStatus = nullptr;
bool installPropertyCache = true;
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)) {
isComponent = true;
QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent);
typeName = QStringLiteral("<component>");
@@ -1275,13 +1313,13 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
compilationUnit.data(), obj, typeName, context->url()));
Q_UNUSED(typeName); // only relevant for tracing
- ddata->lineNumber = obj->location.line;
- ddata->columnNumber = obj->location.column;
+ ddata->lineNumber = obj->location.line();
+ ddata->columnNumber = obj->location.column();
ddata->setImplicitDestructible();
// inline components are root objects, but their index is != 0, so we need
// an additional check
- const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
+ const bool isInlineComponent = obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) {
if (ddata->context) {
Q_ASSERT(ddata->context != context.data());
@@ -1315,7 +1353,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (isContextObject)
context->setContextObject(instance);
- if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) {
+ if (customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) {
customParser->engine = QQmlEnginePrivate::get(engine);
customParser->imports = compilationUnit->typeNameCache.data();
@@ -1323,9 +1361,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
const QV4::CompiledData::Binding *binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
- if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) {
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
bindings << binding;
- }
}
customParser->applyBindings(instance, compilationUnit.data(), bindings);
@@ -1438,16 +1475,24 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
void *argv[] = { &bindable };
// allow interception
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
- bindable.setBinding(qmlBinding);
+ const bool success = bindable.setBinding(qmlBinding);
+
+ // Only pop_front after setting the binding as the bindings are refcounted.
sharedState->allQPropertyBindings.pop_front();
- if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
- auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
- auto jsExpression = qmlBindingPriv->jsExpression();
- const bool canRemove = !qmlBinding.error().hasError() && !qmlBindingPriv->hasDependencies()
- && !jsExpression->hasUnresolvedNames();
- if (canRemove)
- bindable.takeBinding();
+
+ // If the binding was actually not set, it's deleted now.
+ if (success) {
+ if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
+ auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
+ auto jsExpression = qmlBindingPriv->jsExpression();
+ const bool canRemove = !qmlBinding.error().hasError()
+ && !qmlBindingPriv->hasDependencies()
+ && !jsExpression->hasUnresolvedNames();
+ if (canRemove)
+ bindable.takeBinding();
+ }
}
+
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return false;
}
@@ -1519,8 +1564,11 @@ void QQmlObjectCreator::clear()
phase = Done;
}
-bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty)
+bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget,
+ const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding)
{
+ Q_ASSERT(instance);
QQmlData *declarativeData = QQmlData::get(instance, /*create*/true);
qSwap(_qobject, instance);
@@ -1555,9 +1603,10 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
qSwap(_propertyCache, cache);
qSwap(_vmeMetaObject, vmeMetaObject);
- if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings)
+ if (_compiledObject->hasFlag(QV4::CompiledData::Object::HasDeferredBindings))
_ddata->deferData(_compiledObjectIndex, compilationUnit, context);
+ const qsizetype oldRequiredPropertiesCount = sharedState->requiredProperties.size();
QSet<QString> postHocRequired;
for (auto it = _compiledObject->requiredPropertyExtraDataBegin(); it != _compiledObject->requiredPropertyExtraDataEnd(); ++it)
postHocRequired.insert(stringAt(it->nameIndex));
@@ -1568,7 +1617,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex);
// only compute stringAt if there's a chance for the lookup to succeed
auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex));
- if (!property->isRequired && postHocRequired.end() == postHocIt)
+ if (!property->isRequired() && postHocRequired.end() == postHocIt)
continue;
if (postHocIt != postHocRequired.end())
postHocRequired.erase(postHocIt);
@@ -1578,10 +1627,48 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
}
- for (int i = 0; i <= _propertyCache->propertyOffset(); ++i) {
+ const auto getPropertyCacheRange = [&]() -> std::pair<int, int> {
+ // the logic in a nutshell: we work with QML instances here. every
+ // instance has a QQmlType:
+ // * if QQmlType is valid && not an inline component, it's a C++ type
+ // * otherwise, it's a QML-defined type (a.k.a. Composite type), where
+ // invalid type == "comes from another QML document"
+ //
+ // 1. if the type we inherit from comes from C++, we must check *all*
+ // properties in the property cache so far - since we can have
+ // required properties defined in C++
+ // 2. otherwise - the type comes from QML, it's enough to check just
+ // *own* properties in the property cache, because there's a previous
+ // type in the hierarchy that has checked the C++ properties (via 1.)
+ // 3. required attached properties are explicitly not supported. to
+ // achieve that, go through all its properties
+ // 4. required group properties: the group itself is covered by 1.
+ // required sub-properties are not properly handled (QTBUG-96544), so
+ // just return the old range here for consistency
+ QV4::ResolvedTypeReference *typeRef = resolvedType(_compiledObject->inheritedTypeNameIndex);
+ if (!typeRef) { // inside a binding on attached/group property
+ Q_ASSERT(binding);
+ if (binding->isAttachedProperty())
+ return { 0, _propertyCache->propertyCount() }; // 3.
+ Q_ASSERT(binding->isGroupProperty());
+ return { 0, _propertyCache->propertyOffset() + 1 }; // 4.
+ }
+ Q_ASSERT(!_compiledObject->hasFlag(QV4::CompiledData::Object::IsComponent));
+ QQmlType type = typeRef->type();
+ if (type.isValid() && !type.isInlineComponentType()) {
+ return { 0, _propertyCache->propertyCount() }; // 1.
+ }
+ // Q_ASSERT(type.isComposite());
+ return { _propertyCache->propertyOffset(), _propertyCache->propertyCount() }; // 2.
+ };
+ const auto [offset, count] = getPropertyCacheRange();
+ for (int i = offset; i < count; ++i) {
QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i);
if (!propertyData)
continue;
+ // TODO: the property might be a group property (in which case we need
+ // to dive into its sub-properties and check whether there are any
+ // required elements there) - QTBUG-96544
if (!propertyData->isRequired() && postHocRequired.isEmpty())
continue;
QString name = propertyData->name(_qobject);
@@ -1593,8 +1680,43 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
postHocRequired.erase(postHocIt);
sharedState->hadRequiredProperties = true;
- sharedState->requiredProperties.insert(propertyData, RequiredPropertyInfo {name, compilationUnit->finalUrl(), _compiledObject->location, {}});
+ sharedState->requiredProperties.insert(
+ propertyData,
+ RequiredPropertyInfo {
+ name, compilationUnit->finalUrl(), _compiledObject->location, {} });
+ }
+
+ if (binding && binding->isAttachedProperty()
+ && sharedState->requiredProperties.size() != oldRequiredPropertiesCount) {
+ recordError(
+ binding->location,
+ QLatin1String("Attached property has required properties. This is not supported"));
+ }
+
+ // Note: there's a subtle case with the above logic: if we process a random
+ // QML-defined leaf type, it could have a required attribute overwrite on an
+ // *existing* property: `import QtQuick; Text { required text }`. in this
+ // case, we must add the property to a required list
+ if (!postHocRequired.isEmpty()) {
+ // NB: go through [0, offset) range as [offset, count) is already done
+ for (int i = 0; i < offset; ++i) {
+ QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i);
+ if (!propertyData)
+ continue;
+ QString name = propertyData->name(_qobject);
+ auto postHocIt = postHocRequired.find(name);
+ if (postHocRequired.end() == postHocIt)
+ continue;
+ postHocRequired.erase(postHocIt);
+
+ sharedState->hadRequiredProperties = true;
+ sharedState->requiredProperties.insert(
+ propertyData,
+ RequiredPropertyInfo {
+ name, compilationUnit->finalUrl(), _compiledObject->location, {} });
+ }
}
+
if (!postHocRequired.isEmpty() && hadInheritedRequiredProperties)
recordError({}, QLatin1String("Property %1 was marked as required but does not exist").arg(*postHocRequired.begin()));
@@ -1605,12 +1727,12 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) {
const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex;
const auto originalAlias = alias;
- while (alias->aliasToLocalAlias)
+ while (alias->isAliasToLocalAlias())
alias = _compiledObject->aliasesBegin() + alias->localAliasIndex;
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
if (!context->isIdValueSet(0)) // TODO: Do we really want 0 here?
continue;
- QObject *target = context->idValue(alias->targetObjectId);
+ QObject *target = context->idValue(alias->targetObjectId());
if (!target)
continue;
QQmlData *targetDData = QQmlData::get(target, /*create*/false);
@@ -1622,7 +1744,11 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
continue;
auto it = sharedState->requiredProperties.find(targetProperty);
if (it != sharedState->requiredProperties.end())
- it->aliasesToRequired.push_back(AliasToRequiredInfo {compilationUnit->stringAt(originalAlias->nameIndex), compilationUnit->finalUrl()});
+ it->aliasesToRequired.push_back(
+ AliasToRequiredInfo {
+ compilationUnit->stringAt(originalAlias->nameIndex()),
+ compilationUnit->finalUrl()
+ });
}
qSwap(_vmeMetaObject, vmeMetaObject);
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 1881b3eb23..4e788e88d4 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -89,6 +89,12 @@ struct RequiredPropertyInfo
class RequiredProperties : public QHash<QQmlPropertyData*, RequiredPropertyInfo> {};
+struct DeferredQPropertyBinding {
+ QObject *target = nullptr;
+ int properyIndex = -1;
+ QUntypedPropertyBinding binding;
+};
+
struct QQmlObjectCreatorSharedState : QQmlRefCount
{
QQmlRefPointer<QQmlContextData> rootContext;
@@ -102,7 +108,7 @@ struct QQmlObjectCreatorSharedState : QQmlRefCount
QQmlVmeProfiler profiler;
QRecursionNode recursionNode;
RequiredProperties requiredProperties;
- QList<std::tuple<QObject *, int, QUntypedPropertyBinding>> allQPropertyBindings;
+ QList<DeferredQPropertyBinding> allQPropertyBindings;
bool hadRequiredProperties;
};
@@ -155,8 +161,9 @@ private:
QObject *createInstance(int index, QObject *parent = nullptr, bool isContextObject = false);
- bool populateInstance(int index, QObject *instance,
- QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty);
+ bool populateInstance(int index, QObject *instance, QObject *bindingTarget,
+ const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding = nullptr);
// If qmlProperty and binding are null, populate all properties, otherwise only the given one.
void populateDeferred(QObject *instance, int deferredIndex,
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index ec2d57013f..ddd94630d7 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -40,6 +40,7 @@
#include "qqmlopenmetaobject_p.h"
#include <private/qqmlpropertycache_p.h>
#include <private/qqmldata_p.h>
+#include <private/qqmlmetatype_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <qdebug.h>
@@ -427,8 +428,11 @@ void QQmlOpenMetaObject::setCached(bool c)
QQmlData *qmldata = QQmlData::get(d->object, true);
if (d->cacheProperties) {
+ // As the propertyCache is not saved in QQmlMetaType (due to it being dynamic)
+ // we cannot leak it to other places before we're done with it. Yes, it's still
+ // terrible.
if (!d->type->d->cache)
- d->type->d->cache = new QQmlPropertyCache(this);
+ d->type->d->cache = QQmlPropertyCache::createStandalone(this).take();
qmldata->propertyCache = d->type->d->cache;
d->type->d->cache->addref();
} else {
diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp
index c121ea7066..f0cc3d2a5b 100644
--- a/src/qml/qml/qqmlpluginimporter.cpp
+++ b/src/qml/qml/qqmlpluginimporter.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qobject.h>
#include <QtCore/qpluginloader.h>
#include <QtCore/qdir.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qjsonarray.h>
#include <unordered_map>
@@ -336,6 +337,12 @@ QTypeRevision QQmlPluginImporter::importDynamicPlugin(
if (it != plugins->end() && it->second.loader)
instance = it->second.loader->instance();
}
+#else
+ // Here plugin is not optional and NOT QT_CONFIG(library)
+ // Cannot finalize such plugin and return valid, because no types are registered.
+ // Just return invalid.
+ if (!optional)
+ return QTypeRevision();
#endif // QT_CONFIG(library)
}
@@ -423,53 +430,64 @@ QString QQmlPluginImporter::resolvePlugin(const QString &qmldirPluginPath, const
searchPaths.prepend(qmldirPluginPath);
for (const QString &pluginPath : qAsConst(searchPaths)) {
- QString resolvedPath;
+ QString resolvedBasePath;
if (pluginPath == QLatin1String(".")) {
if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty()
&& qmldirPluginPath != QLatin1String(".")) {
- resolvedPath = QDir::cleanPath(qmldirPath + u'/' + qmldirPluginPath);
+ resolvedBasePath = QDir::cleanPath(qmldirPath + u'/' + qmldirPluginPath);
} else {
- resolvedPath = qmldirPath;
+ resolvedBasePath = qmldirPath;
}
} else {
if (QDir::isRelativePath(pluginPath))
- resolvedPath = QDir::cleanPath(qmldirPath + u'/' + pluginPath);
+ resolvedBasePath = QDir::cleanPath(qmldirPath + u'/' + pluginPath);
else
- resolvedPath = pluginPath;
+ resolvedBasePath = pluginPath;
}
// hack for resources, should probably go away
- if (resolvedPath.startsWith(u':'))
- resolvedPath = QCoreApplication::applicationDirPath();
+ if (resolvedBasePath.startsWith(u':'))
+ resolvedBasePath = QCoreApplication::applicationDirPath();
+
+ if (!resolvedBasePath.endsWith(u'/'))
+ resolvedBasePath += u'/';
- if (!resolvedPath.endsWith(u'/'))
- resolvedPath += u'/';
+ QString resolvedPath = resolvedBasePath + prefix + baseName;
+ for (const QString &suffix : suffixes) {
+ const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix);
+ if (!absolutePath.isEmpty())
+ return absolutePath;
+ }
-#if defined(Q_OS_ANDROID)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
+# if defined(Q_OS_ANDROID)
if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':')
- && qmldirPath.at(1) == QLatin1Char('/') &&
- qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"),
- Qt::CaseInsensitive)) {
+ && qmldirPath.at(1) == QLatin1Char('/')
+ && qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"),
+ Qt::CaseInsensitive)) {
QString pluginName = qmldirPath.mid(21) + u'/' + baseName;
pluginName.replace(QLatin1Char('/'), QLatin1Char('_'));
- QString bundledPath = resolvedPath + QLatin1String("lib") + pluginName;
+ QString bundledPath = resolvedBasePath + QLatin1String("lib") + pluginName;
for (const QString &suffix : suffixes) {
const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix);
- if (!absolutePath.isEmpty())
+ if (!absolutePath.isEmpty()) {
+ qWarning("The implicit resolving of Qml plugin locations using the URI "
+ "embedded in the filename has been deprecated. Please use the "
+ "modern CMake API to create QML modules or set the name of "
+ "QML plugin in qmldir file, that matches the name of plugin "
+ "on file system. The correct plugin name is '%s'.",
+ qPrintable(pluginName));
return absolutePath;
+ }
}
}
+# endif
#endif
- resolvedPath += prefix + baseName;
- for (const QString &suffix : suffixes) {
- const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix);
- if (!absolutePath.isEmpty())
- return absolutePath;
- }
}
- qCDebug(lcQmlImport) << "resolvePlugin" << "Could not resolve plugin"
- << baseName << "in" << qmldirPath;
+ qCDebug(lcQmlImport) << "resolvePlugin" << "Could not resolve dynamic plugin with base name"
+ << baseName << "in" << qmldirPath
+ << " file does not exist";
return QString();
}
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index b24b57a0e4..372103b94f 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -74,6 +74,8 @@ Q_DECLARE_METATYPE(QList<QUrl>)
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(compatResolveUrlsOnAssigment, QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT);
+
/*!
\class QQmlProperty
\since 5.0
@@ -246,6 +248,11 @@ QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propert
return result;
}
+bool QQmlPropertyPrivate::resolveUrlsOnAssignment()
+{
+ return ::compatResolveUrlsOnAssigment();
+}
+
QQmlRefPointer<QQmlContextData> QQmlPropertyPrivate::effectiveContext() const
{
if (context)
@@ -287,17 +294,24 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
currentObject = qmlAttachedPropertiesObject(currentObject, func);
if (!currentObject) return; // Something is broken with the attachable type
} else if (r.importNamespace) {
- if ((ii + 1) == path.count()) return; // No type following the namespace
+ if (++ii == path.count())
+ return; // No type following the namespace
- ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace);
- if (!r.type.isValid()) return; // Invalid type in namespace
+ // TODO: Do we really _not_ want to query the namespaced types here?
+ r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>(
+ path.at(ii), r.importNamespace);
+
+ if (!r.type.isValid())
+ return; // Invalid type in namespace
QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
- if (!func) return; // Not an attachable type
+ if (!func)
+ return; // Not an attachable type
currentObject = qmlAttachedPropertiesObject(currentObject, func);
- if (!currentObject) return; // Something is broken with the attachable type
+ if (!currentObject)
+ return; // Something is broken with the attachable type
} else if (r.scriptIndex != -1) {
return; // Not a type
@@ -612,12 +626,8 @@ QObject *QQmlProperty::object() const
*/
QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
{
- if (d)
- d->release();
- d = other.d;
- if (d)
- d->addref();
-
+ QQmlProperty copied(other);
+ qSwap(d, copied.d);
return *this;
}
@@ -1134,32 +1144,38 @@ QVariant QQmlPropertyPrivate::readValueProperty()
}
// helper function to allow assignment / binding to QList<QUrl> properties.
-QVariant QQmlPropertyPrivate::urlSequence(const QVariant &value)
+QList<QUrl> QQmlPropertyPrivate::urlSequence(const QVariant &value)
{
+ if (value.metaType() == QMetaType::fromType<QList<QUrl>>())
+ return value.value<QList<QUrl> >();
+
QList<QUrl> urls;
- if (value.userType() == qMetaTypeId<QUrl>()) {
+ if (value.metaType() == QMetaType::fromType<QUrl>()) {
urls.append(value.toUrl());
- } else if (value.userType() == qMetaTypeId<QString>()) {
+ } else if (value.metaType() == QMetaType::fromType<QString>()) {
urls.append(QUrl(value.toString()));
- } else if (value.userType() == qMetaTypeId<QByteArray>()) {
+ } else if (value.metaType() == QMetaType::fromType<QByteArray>()) {
urls.append(QUrl(QString::fromUtf8(value.toByteArray())));
- } else if (value.userType() == qMetaTypeId<QList<QUrl> >()) {
- urls = value.value<QList<QUrl> >();
- } else if (value.userType() == qMetaTypeId<QStringList>()) {
+ } else if (value.metaType() == QMetaType::fromType<QStringList>()) {
QStringList urlStrings = value.value<QStringList>();
const int urlStringsSize = urlStrings.size();
urls.reserve(urlStringsSize);
for (int i = 0; i < urlStringsSize; ++i)
urls.append(QUrl(urlStrings.at(i)));
- } else if (value.userType() == qMetaTypeId<QList<QString> >()) {
- QList<QString> urlStrings = value.value<QList<QString> >();
- const int urlStringsSize = urlStrings.size();
- urls.reserve(urlStringsSize);
- for (int i = 0; i < urlStringsSize; ++i)
- urls.append(QUrl(urlStrings.at(i)));
} // note: QList<QByteArray> is not currently supported.
+ return urls;
+}
- return QVariant::fromValue<QList<QUrl> >(urls);
+// ### Qt7: Get rid of this
+QList<QUrl> QQmlPropertyPrivate::urlSequence(
+ const QVariant &value, const QQmlRefPointer<QQmlContextData> &ctxt)
+{
+ QList<QUrl> urls = urlSequence(value);
+
+ for (auto urlIt = urls.begin(); urlIt != urls.end(); ++urlIt)
+ *urlIt = ctxt->resolvedUrl(*urlIt);
+
+ return urls;
}
//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
@@ -1293,15 +1309,18 @@ bool QQmlPropertyPrivate::write(
return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
} else if (property.isQObject()) {
QVariant val = value;
- int varType = variantType;
- if (variantType == QMetaType::Nullptr) {
+ const QMetaType variantMetaType = value.metaType();
+ QMetaType varType;
+ if (variantMetaType == QMetaType::fromType<std::nullptr_t>()) {
// This reflects the fact that you can assign a nullptr to a QObject pointer
// Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
- varType = QMetaType::QObjectStar;
+ varType = QMetaType::fromType<QObject*>();
val = QVariant(QMetaType::fromType<QObject *>(), nullptr);
+ } else {
+ varType = variantMetaType;
}
- QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType);
- if (valMo.isNull())
+ QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType.id());
+ if (valMo.isNull() || !varType.flags().testFlag(QMetaType::PointerToQObject))
return false;
QObject *o = *static_cast<QObject *const *>(val.constData());
QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
@@ -1354,8 +1373,11 @@ bool QQmlPropertyPrivate::write(
return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
} else if (isUrl) {
QUrl u;
- if (variantType == QMetaType::QUrl)
+ if (variantType == QMetaType::QUrl) {
u = value.toUrl();
+ if (compatResolveUrlsOnAssigment() && context && u.isRelative() && !u.isEmpty())
+ u = context->resolvedUrl(u);
+ }
else if (variantType == QMetaType::QByteArray)
u = QUrl(QString::fromUtf8(value.toByteArray()));
else if (variantType == QMetaType::QString)
@@ -1365,7 +1387,9 @@ bool QQmlPropertyPrivate::write(
return property.writeProperty(object, &u, flags);
} else if (propertyType == qMetaTypeId<QList<QUrl>>()) {
- QList<QUrl> urlSeq = urlSequence(value).value<QList<QUrl>>();
+ QList<QUrl> urlSeq = compatResolveUrlsOnAssigment()
+ ? urlSequence(value, context)
+ : urlSequence(value);
return property.writeProperty(object, &urlSeq, flags);
} else if (property.isQList()) {
QQmlMetaObject listType;
@@ -1775,3 +1799,5 @@ void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index)
}
QT_END_NAMESPACE
+
+#include "moc_qqmlproperty.cpp"
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index c54d066152..5b42134a1f 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -82,6 +82,9 @@ public:
QString nameCache;
+ // ### Qt7: Get rid of this.
+ static bool resolveUrlsOnAssignment();
+
QQmlPropertyPrivate() {}
QQmlPropertyIndex encodedIndex() const
@@ -161,7 +164,9 @@ public:
int type = 0, int *types = nullptr);
static void flushSignal(const QObject *sender, int signal_index);
- static QVariant urlSequence(const QVariant &value);
+ static QList<QUrl> urlSequence(const QVariant &value);
+ static QList<QUrl> urlSequence(
+ const QVariant &value, const QQmlRefPointer<QQmlContextData> &ctxt);
static QQmlProperty create(
QObject *target, const QString &propertyName,
const QQmlRefPointer<QQmlContextData> &context);
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 185fac6dcc..0cc544e266 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -42,6 +42,9 @@
#include <private/qv4jscall_p.h>
#include <qqmlinfo.h>
#include <QtCore/qloggingcategory.h>
+#include <private/qqmlscriptstring_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qv4qmlcontext_p.h>
QT_BEGIN_NAMESPACE
@@ -91,6 +94,44 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromCodeString(const QQmlProp
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
}
+QUntypedPropertyBinding QQmlPropertyBinding::createFromScriptString(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt, QObject *target, QQmlPropertyIndex targetIndex)
+{
+ const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
+ // without a valid context, we cannot create anything
+ if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) {
+ return {};
+ }
+
+ auto scopeObject = obj ? obj : scriptPrivate->scope;
+
+ QV4::Function *runtimeFunction = nullptr;
+ QString url;
+ QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context);
+ QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
+ if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) {
+ url = ctxtdata->urlString();
+ if (scriptPrivate->bindingId != QQmlBinding::Invalid)
+ runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId);
+ }
+ // Do we actually have a function in the script string? If not, this becomes createCodeFromString
+ if (!runtimeFunction)
+ return createFromCodeString(property, scriptPrivate->script, obj, ctxtdata, url, scriptPrivate->lineNumber, target, targetIndex);
+
+ auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
+ + sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
+ auto binding = new(buffer) QQmlPropertyBinding(QMetaType(property->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
+ auto js = new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
+ Q_ASSERT(binding->jsExpression() == js);
+ Q_ASSERT(js->asBinding() == binding);
+ js->setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
+
+ QV4::ExecutionEngine *v4 = engine->v4engine();
+ QV4::Scope scope(v4);
+ QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, scopeObject));
+ js->setupFunction(qmlContext, runtimeFunction);
+ return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
+}
+
QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
{
auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
@@ -124,8 +165,10 @@ void QQmlPropertyBindingJS::expressionChanged()
{
if (!asBinding()->propertyDataPtr)
return;
+ if (QQmlData::wasDeleted(asBinding()->target()))
+ return;
const auto currentTag = m_error.tag();
- if (currentTag & InEvaluationLoop) {
+ if (currentTag == InEvaluationLoop) {
QQmlError err;
auto location = QQmlJavaScriptExpression::sourceLocation();
err.setUrl(QUrl{location.sourceFile});
@@ -141,15 +184,15 @@ void QQmlPropertyBindingJS::expressionChanged()
qmlWarning(this->scopeObject(), err);
return;
}
- m_error.setTag(currentTag | InEvaluationLoop);
+ m_error.setTag(InEvaluationLoop);
asBinding()->evaluateRecursive();
asBinding()->notifyRecursive();
- m_error.setTag(currentTag);
+ m_error.setTag(NoTag);
}
QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction)
: QPropertyBindingPrivate(mt,
- &QtPrivate::bindingFunctionVTable<QQmlPropertyBinding>,
+ bindingFunctionVTableForQQmlPropertyBinding(mt),
QPropertyBindingSourceLocation(), true)
{
static_assert (std::is_trivially_destructible_v<TargetData>);
@@ -160,165 +203,6 @@ QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlProp
errorCallBack = bindingErrorCallback;
}
-template<typename T>
-bool compareAndAssign(void *dataPtr, const void *result)
-{
- if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
- return false;
- *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
- return true;
-}
-
-bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
-{
- const auto ctxt = jsExpression()->context();
- QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
- if (!engine) {
- QPropertyBindingError error(QPropertyBindingError::EvaluationError);
- if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
- currentBinding->setError(std::move(error));
- return false;
- }
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- ep->referenceScarceResources();
-
- const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
- ep->dereferenceScarceResources();
- if (jsExpression()->hasError()) {
- QPropertyBindingError error(QPropertyBindingError::UnknownError,
- jsExpression()->delayedError()->error().description());
- QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
- bindingErrorCallback(this);
- return false;
- }
-
- if (evaluatedToUndefined) {
- handleUndefinedAssignment(ep, dataPtr);
- // if property has been changed due to reset, reset is responsible for
- // notifying observers
- return false;
- } else if (isUndefined()) {
- setIsUndefined(false);
- }
-
- return true;
- };
-
- if (!hasBoundFunction()) {
- Q_ASSERT(metaType.sizeOf() > 0);
-
- // No need to construct here. evaluate() expects uninitialized memory.
- Q_ALLOCA_VAR(void, result, metaType.sizeOf());
-
- const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
- if (!handleErrorAndUndefined(evaluatedToUndefined))
- return false;
-
- if (metaType.flags() & QMetaType::PointerToQObject)
- return compareAndAssign<QObject *>(dataPtr, result);
-
- switch (metaType.id()) {
- case QMetaType::Bool:
- return compareAndAssign<bool>(dataPtr, result);
- case QMetaType::Int:
- return compareAndAssign<int>(dataPtr, result);
- case QMetaType::Double:
- return compareAndAssign<double>(dataPtr, result);
- case QMetaType::Float:
- return compareAndAssign<float>(dataPtr, result);
- case QMetaType::QString: {
- const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
- static_cast<QString *>(result)->~QString();
- return hasChanged;
- }
- default:
- break;
- }
-
- const bool hasChanged = !metaType.equals(result, dataPtr);
- if (hasChanged) {
- metaType.destruct(dataPtr);
- metaType.construct(dataPtr, result);
- }
- metaType.destruct(result);
- return hasChanged;
- }
-
- bool evaluatedToUndefined = false;
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue result(scope, static_cast<QQmlPropertyBindingJSForBoundFunction *>(
- jsExpression())->evaluate(&evaluatedToUndefined));
-
- if (!handleErrorAndUndefined(evaluatedToUndefined))
- return false;
-
- int propertyType = metaType.id();
-
- switch (propertyType) {
- case QMetaType::Bool: {
- bool b;
- if (result->isBoolean())
- b = result->booleanValue();
- else
- b = result->toBoolean();
- if (b == *static_cast<bool *>(dataPtr))
- return false;
- *static_cast<bool *>(dataPtr) = b;
- return true;
- }
- case QMetaType::Int: {
- int i;
- if (result->isInteger())
- i = result->integerValue();
- else if (result->isNumber()) {
- i = QV4::StaticValue::toInteger(result->doubleValue());
- } else {
- break;
- }
- if (i == *static_cast<int *>(dataPtr))
- return false;
- *static_cast<int *>(dataPtr) = i;
- return true;
- }
- case QMetaType::Double:
- if (result->isNumber()) {
- double d = result->asDouble();
- if (d == *static_cast<double *>(dataPtr))
- return false;
- *static_cast<double *>(dataPtr) = d;
- return true;
- }
- break;
- case QMetaType::Float:
- if (result->isNumber()) {
- float d = float(result->asDouble());
- if (d == *static_cast<float *>(dataPtr))
- return false;
- *static_cast<float *>(dataPtr) = d;
- return true;
- }
- break;
- case QMetaType::QString:
- if (result->isString()) {
- QString s = result->toQStringNoThrow();
- if (s == *static_cast<QString *>(dataPtr))
- return false;
- *static_cast<QString *>(dataPtr) = s;
- return true;
- }
- break;
- default:
- break;
- }
-
- QVariant resultVariant(scope.engine->toVariant(result, metaType));
- resultVariant.convert(metaType);
- const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
- metaType.destruct(dataPtr);
- metaType.construct(dataPtr, resultVariant.constData());
- return hasChanged;
-}
-
static QtPrivate::QPropertyBindingData *bindingDataFromPropertyData(QUntypedPropertyData *dataPtr, QMetaType type)
{
// XXX Qt 7: We need a clean way to access the binding data
@@ -452,8 +336,8 @@ void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(const QQmlPropertyData *pd, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
auto translationBinding = [compilationUnit, binding](const QMetaType &metaType, void *dataPtr) -> bool {
- // Create a dependency to the uiLanguage
- QJSEnginePrivate::get(compilationUnit->engine)->uiLanguage.value();
+ // Create a dependency to the translationLanguage
+ QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
QVariant resultVariant(compilationUnit->bindingValueAsString(binding));
if (metaType.id() != QMetaType::QString)
diff --git a/src/qml/qml/qqmlpropertybinding_p.h b/src/qml/qml/qqmlpropertybinding_p.h
index 341e300de4..17f74d5675 100644
--- a/src/qml/qml/qqmlpropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertybinding_p.h
@@ -56,6 +56,7 @@
#include "qqmlpropertydata_p.h"
#include "qqmljavascriptexpression_p.h"
+#include <private/qv4alloca_p.h>
#include <memory>
@@ -69,6 +70,8 @@ class QQmlPropertyBinding;
class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
{
+ bool mustCaptureBindableProperty() const final {return false;}
+
friend class QQmlPropertyBinding;
void expressionChanged() override;
QQmlPropertyBinding *asBinding()
@@ -126,6 +129,10 @@ public:
const QQmlRefPointer<QQmlContextData> &ctxt,
const QString &url, quint16 lineNumber,
QObject *target, QQmlPropertyIndex targetIndex);
+ static QUntypedPropertyBinding createFromScriptString(const QQmlPropertyData *property,
+ const QQmlScriptString& script, QObject *obj,
+ QQmlContext *ctxt, QObject *target,
+ QQmlPropertyIndex targetIndex);
static QUntypedPropertyBinding createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function,
QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
@@ -144,11 +151,12 @@ public:
return static_cast<const QQmlPropertyBinding *>(binding)->isUndefined();
}
+ template<QMetaType::Type type>
static bool doEvaluate(QMetaType metaType, QUntypedPropertyData *dataPtr, void *f) {
auto address = static_cast<std::byte*>(f);
address -= QPropertyBindingPrivate::getSizeEnsuringAlignment(); // f now points to QPropertyBindingPrivate suboject
// and that has the same address as QQmlPropertyBinding
- return reinterpret_cast<QQmlPropertyBinding *>(address)->evaluate(metaType, dataPtr);
+ return reinterpret_cast<QQmlPropertyBinding *>(address)->evaluate<type>(metaType, dataPtr);
}
bool hasDependencies()
@@ -157,6 +165,7 @@ public:
}
private:
+ template <QMetaType::Type type>
bool evaluate(QMetaType metaType, void *dataPtr);
Q_NEVER_INLINE void handleUndefinedAssignment(QQmlEnginePrivate *ep, void *dataPtr);
@@ -210,9 +219,9 @@ template <auto I>
struct Print {};
namespace QtPrivate {
-template<>
-inline constexpr BindingFunctionVTable bindingFunctionVTable<QQmlPropertyBinding> = {
- &QQmlPropertyBinding::doEvaluate,
+template<QMetaType::Type type>
+inline constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyBinding = {
+ &QQmlPropertyBinding::doEvaluate<type>,
[](void *qpropertyBinding){
QQmlPropertyBinding *binding = reinterpret_cast<QQmlPropertyBinding *>(qpropertyBinding);
binding->jsExpression()->~QQmlPropertyBindingJS();
@@ -225,6 +234,24 @@ inline constexpr BindingFunctionVTable bindingFunctionVTable<QQmlPropertyBinding
};
}
+inline const QtPrivate::BindingFunctionVTable *bindingFunctionVTableForQQmlPropertyBinding(QMetaType type)
+{
+#define FOR_TYPE(TYPE) \
+ case TYPE: return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<TYPE>
+ switch (type.id()) {
+ FOR_TYPE(QMetaType::Int);
+ FOR_TYPE(QMetaType::QString);
+ FOR_TYPE(QMetaType::Double);
+ FOR_TYPE(QMetaType::Float);
+ FOR_TYPE(QMetaType::Bool);
+ default:
+ if (type.flags() & QMetaType::PointerToQObject)
+ return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::QObjectStar>;
+ return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::UnknownType>;
+ }
+#undef FOR_TYPE
+}
+
class QQmlTranslationPropertyBinding
{
public:
@@ -242,6 +269,173 @@ inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
}
static_assert(sizeof(QQmlPropertyBinding) == sizeof(QPropertyBindingPrivate)); // else the whole offset computatation will break
+template<typename T>
+bool compareAndAssign(void *dataPtr, const void *result)
+{
+ if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
+ return false;
+ *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
+ return true;
+}
+
+template <QMetaType::Type type>
+bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
+{
+ const auto ctxt = jsExpression()->context();
+ QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
+ if (!engine) {
+ QPropertyBindingError error(QPropertyBindingError::EvaluationError);
+ if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
+ currentBinding->setError(std::move(error));
+ return false;
+ }
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ ep->referenceScarceResources();
+
+ const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
+ ep->dereferenceScarceResources();
+ if (jsExpression()->hasError()) {
+ QPropertyBindingError error(QPropertyBindingError::UnknownError,
+ jsExpression()->delayedError()->error().description());
+ QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
+ bindingErrorCallback(this);
+ return false;
+ }
+
+ if (evaluatedToUndefined) {
+ handleUndefinedAssignment(ep, dataPtr);
+ // if property has been changed due to reset, reset is responsible for
+ // notifying observers
+ return false;
+ } else if (isUndefined()) {
+ setIsUndefined(false);
+ }
+
+ return true;
+ };
+
+ if (!hasBoundFunction()) {
+ Q_ASSERT(metaType.sizeOf() > 0);
+
+ // No need to construct here. evaluate() expects uninitialized memory.
+ const auto size = [&]() -> qsizetype {
+ switch (type) {
+ case QMetaType::QObjectStar: return sizeof(QObject *);
+ case QMetaType::Bool: return sizeof(bool);
+ case QMetaType::Int: return (sizeof(int));
+ case QMetaType::Double: return (sizeof(double));
+ case QMetaType::Float: return (sizeof(float));
+ case QMetaType::QString: return (sizeof(QString));
+ default: return metaType.sizeOf();
+ }
+ }();
+ Q_ALLOCA_VAR(void, result, size);
+
+ const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
+ if (!handleErrorAndUndefined(evaluatedToUndefined))
+ return false;
+
+ switch (type) {
+ case QMetaType::QObjectStar:
+ return compareAndAssign<QObject *>(dataPtr, result);
+ case QMetaType::Bool:
+ return compareAndAssign<bool>(dataPtr, result);
+ case QMetaType::Int:
+ return compareAndAssign<int>(dataPtr, result);
+ case QMetaType::Double:
+ return compareAndAssign<double>(dataPtr, result);
+ case QMetaType::Float:
+ return compareAndAssign<float>(dataPtr, result);
+ case QMetaType::QString: {
+ const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
+ static_cast<QString *>(result)->~QString();
+ return hasChanged;
+ }
+ default:
+ break;
+ }
+
+ const bool hasChanged = !metaType.equals(result, dataPtr);
+ if (hasChanged) {
+ metaType.destruct(dataPtr);
+ metaType.construct(dataPtr, result);
+ }
+ metaType.destruct(result);
+ return hasChanged;
+ }
+
+ bool evaluatedToUndefined = false;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue result(scope, static_cast<QQmlPropertyBindingJSForBoundFunction *>(
+ jsExpression())->evaluate(&evaluatedToUndefined));
+
+ if (!handleErrorAndUndefined(evaluatedToUndefined))
+ return false;
+
+ switch (type) {
+ case QMetaType::Bool: {
+ bool b;
+ if (result->isBoolean())
+ b = result->booleanValue();
+ else
+ b = result->toBoolean();
+ if (b == *static_cast<bool *>(dataPtr))
+ return false;
+ *static_cast<bool *>(dataPtr) = b;
+ return true;
+ }
+ case QMetaType::Int: {
+ int i;
+ if (result->isInteger())
+ i = result->integerValue();
+ else if (result->isNumber()) {
+ i = QV4::StaticValue::toInteger(result->doubleValue());
+ } else {
+ break;
+ }
+ if (i == *static_cast<int *>(dataPtr))
+ return false;
+ *static_cast<int *>(dataPtr) = i;
+ return true;
+ }
+ case QMetaType::Double:
+ if (result->isNumber()) {
+ double d = result->asDouble();
+ if (d == *static_cast<double *>(dataPtr))
+ return false;
+ *static_cast<double *>(dataPtr) = d;
+ return true;
+ }
+ break;
+ case QMetaType::Float:
+ if (result->isNumber()) {
+ float d = float(result->asDouble());
+ if (d == *static_cast<float *>(dataPtr))
+ return false;
+ *static_cast<float *>(dataPtr) = d;
+ return true;
+ }
+ break;
+ case QMetaType::QString:
+ if (result->isString()) {
+ QString s = result->toQStringNoThrow();
+ if (s == *static_cast<QString *>(dataPtr))
+ return false;
+ *static_cast<QString *>(dataPtr) = s;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ QVariant resultVariant(scope.engine->toVariant(result, metaType));
+ resultVariant.convert(metaType);
+ const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
+ metaType.destruct(dataPtr);
+ metaType.construct(dataPtr, resultVariant.constData());
+ return hasChanged;
+}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 269f02afbd..2ebba1eb72 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -51,8 +51,8 @@
#include <QtCore/qdebug.h>
#include <QtCore/QCryptographicHash>
+#include <QtCore/private/qtools_p.h>
-#include <ctype.h> // for toupper
#include <limits.h>
#include <algorithm>
@@ -159,14 +159,23 @@ QQmlPropertyCache::QQmlPropertyCache()
}
/*!
-Creates a new QQmlPropertyCache of \a metaObject.
+ Creates a standalone QQmlPropertyCache of \a metaObject. It is separate from the usual
+ QQmlPropertyCache hierarchy. Its parent is not equal to any other QQmlPropertyCache
+ created from QObject::staticMetaObject, for example.
*/
-QQmlPropertyCache::QQmlPropertyCache(const QMetaObject *metaObject, QTypeRevision metaObjectRevision)
- : QQmlPropertyCache()
+QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCache::createStandalone(
+ const QMetaObject *metaObject, QTypeRevision metaObjectRevision)
{
Q_ASSERT(metaObject);
- update(metaObject);
+ QQmlRefPointer<QQmlPropertyCache> result;
+ if (const QMetaObject *super = metaObject->superClass()) {
+ result.adopt(createStandalone(super, metaObjectRevision)
+ ->copyAndAppend(metaObject, metaObjectRevision));
+ } else {
+ result.adopt(new QQmlPropertyCache());
+ result->update(metaObject);
+ }
if (metaObjectRevision.isValid() && metaObjectRevision != QTypeRevision::zero()) {
// Set the revision of the meta object that this cache describes to be
@@ -174,9 +183,13 @@ QQmlPropertyCache::QQmlPropertyCache(const QMetaObject *metaObject, QTypeRevisio
// from a type that was created directly in C++, and not through QML. For such
// types, the revision for each recorded QMetaObject would normally be zero, which
// would exclude any revisioned properties.
- for (int metaObjectOffset = 0; metaObjectOffset < allowedRevisionCache.size(); ++metaObjectOffset)
- allowedRevisionCache[metaObjectOffset] = metaObjectRevision;
+ for (int metaObjectOffset = 0; metaObjectOffset < result->allowedRevisionCache.size();
+ ++metaObjectOffset) {
+ result->allowedRevisionCache[metaObjectOffset] = metaObjectRevision;
+ }
}
+
+ return result;
}
QQmlPropertyCache::~QQmlPropertyCache()
@@ -350,7 +363,7 @@ const QMetaObject *QQmlPropertyCache::createMetaObject()
QQmlPropertyData *QQmlPropertyCache::maybeUnresolvedProperty(int index) const
{
- if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
+ if (index < 0 || index >= propertyCount())
return nullptr;
QQmlPropertyData *rv = nullptr;
@@ -514,7 +527,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QVarLengthArray<char, 128> str(length+3);
str[0] = 'o';
str[1] = 'n';
- str[2] = toupper(rawName[0]);
+ str[2] = QtMiscUtils::toAsciiUpper(rawName[0]);
if (length > 1)
memcpy(&str[3], &rawName[1], length - 1);
str[length + 2] = '\0';
@@ -593,16 +606,6 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
}
-void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject)
-{
- if (!metaObject)
- return;
-
- updateRecur(metaObject->superClass());
-
- append(metaObject, QTypeRevision());
-}
-
void QQmlPropertyCache::update(const QMetaObject *metaObject)
{
Q_ASSERT(metaObject);
@@ -622,7 +625,8 @@ void QQmlPropertyCache::update(const QMetaObject *metaObject)
// cached in a parent cache.
stringCache.reserve(pc + mc + sc);
- updateRecur(metaObject);
+ if (metaObject)
+ append(metaObject, QTypeRevision());
}
/*! \internal
@@ -676,7 +680,8 @@ inline bool contextHasNoExtensions(const QQmlRefPointer<QQmlContextData> &contex
{
// This context has no extension if its parent is the engine's rootContext,
// which has children but no imports
- return (!context->parent() || !context->parent()->imports());
+ const QQmlRefPointer<QQmlContextData> parent = context->parent();
+ return (!parent || !parent->imports());
}
inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount)
@@ -1062,6 +1067,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
property.setWritable(data->isWritable());
property.setResettable(data->isResettable());
property.setBindable(data->isBindable());
+ property.setAlias(data->isAlias());
}
for (int ii = 0; ii < methods.count(); ++ii) {
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index f3f81fbc78..689ce8d35f 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -129,7 +129,10 @@ class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount
{
public:
QQmlPropertyCache();
- QQmlPropertyCache(const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
+
+ static QQmlRefPointer<QQmlPropertyCache> createStandalone(
+ const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
+
~QQmlPropertyCache() override;
void update(const QMetaObject *);
@@ -246,8 +249,6 @@ private:
QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *,
const QQmlRefPointer<QQmlContextData> &) const;
- void updateRecur(const QMetaObject *);
-
template<typename K>
QQmlPropertyData *findNamedProperty(const K &key) const
{
@@ -326,7 +327,7 @@ inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
inline QQmlPropertyData *QQmlPropertyCache::property(int index) const
{
- if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
+ if (index < 0 || index >= propertyCount())
return nullptr;
if (index < propertyIndexCacheStart)
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index ff607add09..f6bedc94e9 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -110,11 +110,15 @@ QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencing
bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
{
- if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ if (!instantiatingBinding
+ || instantiatingBinding->type() != QV4::CompiledData::Binding::Type_GroupProperty) {
return true;
+ }
+
+ if (!referencingObjectPropertyCache)
+ return false;
Q_ASSERT(referencingObjectIndex >= 0);
- Q_ASSERT(referencingObjectPropertyCache);
Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
bool notInRevision = false;
@@ -147,6 +151,11 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePr
if (propertyCaches->at(groupPropertyObjectIndex))
continue;
+ if (!pendingBinding.referencingObjectPropertyCache) {
+ pendingBinding.referencingObjectPropertyCache
+ = propertyCaches->at(pendingBinding.referencingObjectIndex);
+ }
+
if (!pendingBinding.resolveInstantiatingProperty())
continue;
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 0a5e642c9f..c415b5f33a 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -67,8 +67,8 @@ inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location,
const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
return error;
}
@@ -243,7 +243,7 @@ QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectsIncrementally()
// create meta objects for inline components before compiling actual root component
if (nodeIt != nodesSorted.rend()) {
- const auto &ic = allICs[nodeIt->index];
+ const auto &ic = allICs[nodeIt->index()];
QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr);
Q_ASSERT(typeRef->typePropertyCache().isNull()); // not set yet
@@ -290,7 +290,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
|| obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
- || (((obj->flags & QV4::CompiledData::Object::IsComponent)
+ || ((obj->hasFlag(QV4::CompiledData::Object::IsComponent)
|| (objectIndex == 0 && isAddressable(objectContainer->url())))
&& !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType());
@@ -298,7 +298,8 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
auto binding = obj->bindingsBegin();
auto end = obj->bindingsEnd();
for ( ; binding != end; ++binding) {
- if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Object
+ && (binding->flags() & QV4::CompiledData::Binding::IsOnAssignment)) {
// If the on assignment is inside a group property, we need to distinguish between QObject based
// group properties and value type group properties. For the former the base type is derived from
// the property that references us, for the latter we only need a meta-object on the referencing object
@@ -340,24 +341,24 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
}
}
- if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
- auto binding = obj->bindingsBegin();
- auto end = obj->bindingsEnd();
- for ( ; binding != end; ++binding)
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
- QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
-
- // Binding to group property where we failed to look up the type of the
- // property? Possibly a group property that is an alias that's not resolved yet.
- // Let's attempt to resolve it after we're done with the aliases and fill in the
- // propertyCaches entry then.
- if (!context.resolveInstantiatingProperty())
- pendingGroupPropertyBindings->append(context);
-
- QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
- if (error.isValid())
- return error;
- }
+ QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex);
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for ( ; binding != end; ++binding) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
+ QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+
+ // Binding to group property where we failed to look up the type of the
+ // property? Possibly a group property that is an alias that's not resolved yet.
+ // Let's attempt to resolve it after we're done with the aliases and fill in the
+ // propertyCaches entry then.
+ if (!thisCache || !context.resolveInstantiatingProperty())
+ pendingGroupPropertyBindings->append(context);
+
+ QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
+ if (error.isValid())
+ return error;
+ }
}
QQmlError noError;
@@ -448,7 +449,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
auto aend = obj->aliasesEnd();
for ( ; a != aend; ++a) {
bool notInRevision = false;
- QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
+ QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex()), &notInRevision);
if (d && d->isFinal())
return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
}
@@ -496,7 +497,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
for ( ; a != aend; ++a) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
+ QString changedSigName = stringAt(a->nameIndex()) + QLatin1String("Changed");
seenSignals.insert(changedSigName);
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
@@ -606,15 +607,14 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
if (type == QV4::CompiledData::BuiltinType::Var)
propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
-
if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
propertyType = metaTypeForPropertyType(type);
} else {
- Q_ASSERT(!p->isBuiltinType);
+ Q_ASSERT(!p->isBuiltinType());
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr,
+ if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
@@ -646,13 +646,13 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
typeIds = compilationUnit->typeIdsForComponent();
}
- if (p->isList) {
+ if (p->isList()) {
propertyType = typeIds.listId;
} else {
propertyType = typeIds.id;
}
} else {
- if (p->isList) {
+ if (p->isList()) {
propertyType = qmltype.qListTypeId();
} else {
propertyType = qmltype.typeId();
@@ -660,18 +660,18 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
}
}
- if (p->isList)
+ if (p->isList())
propertyFlags.type = QQmlPropertyData::Flags::QListType;
else
propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
}
- if (!p->isReadOnly && !p->isList)
+ if (!p->isReadOnly() && !p->isList())
propertyFlags.setIsWritable(true);
QString propertyName = stringAt(p->nameIndex);
- if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ if (!obj->hasAliasAsDefaultProperty() && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
cache->_defaultPropertyName = propertyName;
cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
propertyType, propertyTypeVersion, effectiveSignalIndex);
@@ -687,13 +687,14 @@ template <typename ObjectContainer>
inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
QString *customTypeName)
{
- if (param.indexIsBuiltinType) {
+ if (param.indexIsBuiltinType()) {
// built-in type
- return metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType)));
+ return metaTypeForPropertyType(
+ static_cast<QV4::CompiledData::BuiltinType>(param.typeNameIndexOrBuiltinType()));
}
// lazily resolved type
- const QString typeName = stringAt(param.typeNameIndexOrBuiltinType);
+ const QString typeName = stringAt(param.typeNameIndexOrBuiltinType());
if (customTypeName)
*customTypeName = typeName;
QQmlType qmltype;
@@ -702,8 +703,15 @@ inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter
QQmlType::AnyRegistrationType, &selfReference))
return QMetaType();
- if (!qmltype.isComposite())
- return qmltype.typeId();
+ if (!qmltype.isComposite()) {
+ const QMetaType typeId = qmltype.typeId();
+ if (!typeId.isValid() && qmltype.isInlineComponentType()) {
+ const int objectId = qmltype.inlineComponentId();
+ return objectContainer->typeIdsForComponent(objectId).id;
+ } else {
+ return typeId;
+ }
+ }
if (selfReference)
return objectContainer->typeIdsForComponent().id;
@@ -756,7 +764,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
// from a binding.
for (int i = 1; i < objectContainer->objectCount(); ++i) {
const CompiledObject &component = *objectContainer->objectAt(i);
- if (!(component.flags & QV4::CompiledData::Object::IsComponent))
+ if (!component.hasFlag(QV4::CompiledData::Object::IsComponent))
continue;
const auto rootBinding = component.bindingsBegin();
@@ -779,12 +787,12 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
auto alias = object.aliasesBegin();
auto end = object.aliasesEnd();
for ( ; alias != end; ++alias) {
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
- const int targetObjectIndex = objectForId(component, alias->targetObjectId);
+ const int targetObjectIndex = objectForId(component, alias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
- if (alias->aliasToLocalAlias)
+ if (alias->isAliasToLocalAlias())
continue;
if (alias->encodedMetaPropertyIndex == -1)
@@ -826,18 +834,21 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
objectsWithAliases->append(objectIndex);
// Stop at Component boundary
- if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
+ if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
return;
auto binding = object.bindingsBegin();
auto end = object.bindingsEnd();
for (; binding != end; ++binding) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object
- && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
- continue;
-
- collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ break;
+ default:
+ break;
+ }
}
}
@@ -854,12 +865,12 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
propertyFlags->setIsAlias(true);
- if (alias.aliasToLocalAlias) {
+ if (alias.isAliasToLocalAlias()) {
const QV4::CompiledData::Alias *lastAlias = &alias;
QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
do {
- const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId);
+ const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
Q_ASSERT(targetObject);
@@ -876,17 +887,17 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
seenAliases.append(targetAlias);
lastAlias = targetAlias;
- } while (lastAlias->aliasToLocalAlias);
+ } while (lastAlias->isAliasToLocalAlias());
return propertyDataForAlias(component, *lastAlias, type, version, propertyFlags, enginePriv);
}
- const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
if (alias.encodedMetaPropertyIndex == -1) {
- Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ Q_ASSERT(alias.hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject));
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
if (!typeRef) {
// Can be caused by the alias target not being a valid id or property. E.g.:
@@ -897,10 +908,16 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
}
const auto referencedType = typeRef->type();
- if (referencedType.isValid())
+ if (referencedType.isValid()) {
*type = referencedType.typeId();
- else
+ if (!type->isValid() && referencedType.isInlineComponentType()) {
+ int objectId = referencedType.inlineComponentId();
+ *type = objectContainer->typeIdsForComponent(objectId).id;
+ Q_ASSERT(type->isValid());
+ }
+ } else {
*type = typeRef->compilationUnit()->typeIds.id;
+ }
*version = typeRef->version();
@@ -960,7 +977,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
}
}
- propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable);
+ propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly))
+ && writable);
propertyFlags->setIsResettable(resettable);
propertyFlags->setIsBindable(bindable);
return QQmlError();
@@ -984,7 +1002,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
auto alias = object.aliasesBegin();
auto end = object.aliasesEnd();
for ( ; alias != end; ++alias, ++aliasIndex) {
- Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
QMetaType type;
QTypeRevision version = QTypeRevision::zero();
@@ -994,9 +1012,9 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
if (error.isValid())
return error;
- const QString propertyName = objectContainer->stringAt(alias->nameIndex);
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex());
- if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
propertyCache->_defaultPropertyName = propertyName;
propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
@@ -1012,7 +1030,7 @@ inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const Com
for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
const int candidateIndex = component.namedObjectsInComponentTable()[i];
const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
- if (candidate.id == id)
+ if (candidate.objectId() == id)
return candidateIndex;
}
return -1;
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index edf777ff18..1fbf24a1a7 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -281,7 +281,6 @@ public:
{
Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- Q_ASSERT(idx != m_coreIndex);
m_overrideIndex = qint16(idx);
}
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index f1826b2f1c..6a36c279e8 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -106,10 +106,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return errors;
}
- if (obj->flags & QV4::CompiledData::Object::IsComponent && !(obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)) {
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)
+ && !obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot)) {
Q_ASSERT(obj->nBindings == 1);
const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
return validateObject(componentBinding->value.objectIndex, componentBinding);
}
@@ -134,7 +135,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!binding->isGroupProperty())
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment))
continue;
if (populatingValueTypeGroupProperty) {
@@ -163,9 +164,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
QString name = stringAt(binding->propertyNameIndex);
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
if (customParser) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
customBindings << binding;
continue;
@@ -178,13 +181,14 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
}
bool bindingToDefaultProperty = false;
- bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
+ bool isGroupProperty = instantiatingBinding
+ && instantiatingBinding->type() == QV4::CompiledData::Binding::Type_GroupProperty;
bool notInRevision = false;
QQmlPropertyData *pd = nullptr;
if (!name.isEmpty()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
pd = propertyResolver.signal(name, &notInRevision);
} else {
pd = propertyResolver.property(name, &notInRevision,
@@ -228,11 +232,12 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(binding->location, tr("Invalid attached object assignment"));
}
- if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ if (bindingType >= QV4::CompiledData::Binding::Type_Object
+ && (pd || binding->isAttachedProperty())) {
const bool populatingValueTypeGroupProperty
= pd
&& QQmlMetaType::metaObjectForValueType(pd->propType())
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
+ && !binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment);
const QVector<QQmlError> subObjectValidatorErrors
= validateObject(binding->value.objectIndex, binding,
populatingValueTypeGroupProperty);
@@ -241,13 +246,13 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
}
// Signal handlers were resolved and checked earlier in the signal handler conversion pass.
- if (binding->flags & (QV4::CompiledData::Binding::IsSignalHandlerExpression
+ if (binding->flags() & (QV4::CompiledData::Binding::IsSignalHandlerExpression
| QV4::CompiledData::Binding::IsSignalHandlerObject
| QV4::CompiledData::Binding::IsPropertyObserver)) {
continue;
}
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
return recordError(binding->location, tr("Attached properties cannot be used here"));
}
@@ -261,15 +266,15 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!pd->isWritable()
&& !pd->isQList()
&& !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ && !(bindingFlags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
) {
- if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
+ if (assigningToGroupProperty && bindingType < QV4::CompiledData::Binding::Type_Object)
return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
}
- if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
+ if (!pd->isQList() && (bindingFlags & QV4::CompiledData::Binding::IsListItem)) {
QString error;
if (pd->propType() == QMetaType::fromType<QQmlScriptString>())
error = tr( "Cannot assign multiple values to a script property");
@@ -280,7 +285,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
if (!bindingToDefaultProperty
&& !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment)
&& assigningToGroupProperty) {
QV4::CompiledData::Location loc = binding->valueLocation;
if (loc < (*assignedGroupProperty)->valueLocation)
@@ -291,11 +296,11 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
}
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType < QV4::CompiledData::Binding::Type_Script) {
QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding);
if (bindingError.isValid())
return recordError(bindingError);
- } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ } else if (bindingType == QV4::CompiledData::Binding::Type_Object) {
QQmlError bindingError = validateObjectBinding(pd, name, binding);
if (bindingError.isValid())
return recordError(bindingError);
@@ -378,7 +383,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
QQmlError noError;
if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum))
return noError;
QString value = compilationUnit->bindingValueAsString(binding);
@@ -396,11 +401,13 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
auto warnOrError = [&](const QString &error) {
- if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
QQmlError warning;
warning.setUrl(compilationUnit->url());
- warning.setLine(qmlConvertSourceCoordinate<quint32, int>(binding->valueLocation.line));
- warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(binding->valueLocation.column));
+ warning.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ binding->valueLocation.line()));
+ warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ binding->valueLocation.column()));
warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
"is deprecated. This will become a compile error in "
"future versions of Qt."));
@@ -410,10 +417,11 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
return qQmlCompileError(binding->valueLocation, error);
};
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
const auto isStringBinding = [&]() -> bool {
// validateLiteralBinding is not supposed to be used on scripts
- Q_ASSERT(binding->type != QV4::CompiledData::Binding::Type_Script);
- return binding->type == QV4::CompiledData::Binding::Type_String;
+ Q_ASSERT(bindingType != QV4::CompiledData::Binding::Type_Script);
+ return bindingType == QV4::CompiledData::Binding::Type_String;
};
switch (property->propType().id()) {
@@ -432,19 +440,17 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::QByteArray: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String)
return warnOrError(tr("Invalid property assignment: byte array expected"));
- }
}
break;
case QMetaType::QUrl: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String)
return warnOrError(tr("Invalid property assignment: url expected"));
- }
}
break;
case QMetaType::UInt: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(uint(d)) == d)
return noError;
@@ -453,7 +459,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Int: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(int(d)) == d)
return noError;
@@ -462,13 +468,13 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Float: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number expected"));
}
}
break;
case QMetaType::Double: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number expected"));
}
}
@@ -566,7 +572,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
}
break;
case QMetaType::Bool: {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
return warnOrError(tr("Invalid property assignment: boolean expected"));
}
}
@@ -598,12 +604,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
default: {
// generate single literal value assignment to a list property if required
if (property->propType() == QMetaType::fromType<QList<qreal> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number or array of numbers expected"));
}
break;
} else if (property->propType() == QMetaType::fromType<QList<int> >()) {
- bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
+ bool ok = (bindingType == QV4::CompiledData::Binding::Type_Number);
if (ok) {
double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) != n)
@@ -613,12 +619,12 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
return warnOrError(tr("Invalid property assignment: int or array of ints expected"));
break;
} else if (property->propType() == QMetaType::fromType<QList<bool> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
return warnOrError(tr("Invalid property assignment: bool or array of bools expected"));
}
break;
} else if (property->propType() == QMetaType::fromType<QList<QUrl> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ if (bindingType != QV4::CompiledData::Binding::Type_String) {
return warnOrError(tr("Invalid property assignment: url or array of urls expected"));
}
break;
@@ -632,7 +638,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *prope
} else if (property->propType() == QMetaType::fromType<QQmlScriptString>()) {
break;
} else if (property->isQObject()
- && binding->type == QV4::CompiledData::Binding::Type_Null) {
+ && bindingType == QV4::CompiledData::Binding::Type_Null) {
break;
}
@@ -690,8 +696,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert
{
QQmlError noError;
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) {
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Object);
bool isValueSource = false;
bool isPropertyInterceptor = false;
@@ -740,7 +746,8 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert
}
}
return noError;
- } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
+ } else if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject)
+ && property->isFunction()) {
return noError;
} else if (isPrimitiveType(propType)) {
auto typeName = QString::fromUtf8(QMetaType(propType).name());
diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
index be061ba6cd..9ef8360d2d 100644
--- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
+++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
@@ -54,6 +54,7 @@
#include <private/qtqmlglobal_p.h>
#include <private/qqmlpropertyindex_p.h>
#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp
index 21d3c7ed05..6f48146fa3 100644
--- a/src/qml/qml/qqmlproxymetaobject.cpp
+++ b/src/qml/qml/qqmlproxymetaobject.cpp
@@ -68,11 +68,8 @@ QQmlProxyMetaObject::~QQmlProxyMetaObject()
QObject *QQmlProxyMetaObject::getProxy(int index)
{
if (!proxies) {
- if (!proxies) {
- proxies = new QObject*[metaObjects->count()];
- ::memset(proxies, 0,
- sizeof(QObject *) * metaObjects->count());
- }
+ proxies = new QObject *[metaObjects->count()];
+ ::memset(proxies, 0, sizeof(QObject *) * metaObjects->count());
}
if (!proxies[index]) {
@@ -112,6 +109,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void
const int globalPropertyOffset = metaObjects->at(ii).propertyOffset;
if (id >= globalPropertyOffset) {
QObject *proxy = getProxy(ii);
+ Q_ASSERT(proxy);
const int localProxyOffset = proxy->metaObject()->propertyOffset();
const int localProxyId = id - globalPropertyOffset + localProxyOffset;
@@ -129,7 +127,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void
const int globalMethodOffset = metaObjects->at(ii).methodOffset;
if (id >= globalMethodOffset) {
QObject *proxy = getProxy(ii);
-
+ Q_ASSERT(proxy);
const int localMethodOffset = proxy->metaObject()->methodOffset();
const int localMethodId = id - globalMethodOffset + localMethodOffset;
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index 7b7a842d04..9e2b02162f 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -169,8 +169,8 @@ void QQmlScriptBlob::done()
QList<QQmlError> errors = script.script->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column()));
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
errors.prepend(error);
setError(errors);
@@ -238,8 +238,8 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(import->location.line());
+ error.setColumn(import->location.column());
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return;
diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp
index 3230e69b6d..4df3a63785 100644
--- a/src/qml/qml/qqmlscriptstring.cpp
+++ b/src/qml/qml/qqmlscriptstring.cpp
@@ -220,3 +220,5 @@ bool QQmlScriptString::booleanLiteral(bool *ok) const
QT_END_NAMESPACE
+#include "moc_qqmlscriptstring.cpp"
+
diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h
index a768dabf9f..d2ccfe5bc6 100644
--- a/src/qml/qml/qqmlscriptstring.h
+++ b/src/qml/qml/qqmlscriptstring.h
@@ -86,6 +86,7 @@ private:
friend class QQmlScriptStringPrivate;
friend class QQmlExpression;
friend class QQmlBinding;
+ friend class QQmlPropertyBinding;
friend struct QV4::QObjectWrapper;
};
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index 811ff668c6..c283f7c718 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -216,16 +216,15 @@ void QQmlTypePrivate::init() const
return;
}
- auto setupExtendedMetaObject = [&](
- const QMetaObject *extMetaObject,
- QObject *(*extFunc)(QObject *)) {
-
+ auto setupExtendedMetaObject = [&](const QMetaObject *extMetaObject,
+ QObject *(*extFunc)(QObject *)) {
if (!extMetaObject)
return;
// XXX - very inefficient
QMetaObjectBuilder builder;
- QQmlMetaType::clone(builder, extMetaObject, extMetaObject, extMetaObject);
+ QQmlMetaType::clone(builder, extMetaObject, extMetaObject, extMetaObject,
+ extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly);
builder.setFlags(MetaObjectFlag::DynamicMetaObject);
QMetaObject *mmo = builder.toMetaObject();
mmo->d.superdata = mo;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 839d417a56..d72fdb4d5c 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -151,11 +151,13 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
document->jsModule.fileName = typeData->urlString();
document->jsModule.finalUrl = typeData->finalUrlString();
QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames());
- if (!v4CodeGenerator.generateCodeForComponents(componentRoots())) {
- recordError(v4CodeGenerator.error());
- return nullptr;
+ for (QmlIR::Object *object : qAsConst(document->objects)) {
+ if (!v4CodeGenerator.generateRuntimeFunctions(object)) {
+ Q_ASSERT(v4CodeGenerator.hasError());
+ recordError(v4CodeGenerator.error());
+ return nullptr;
+ }
}
-
document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
}
@@ -180,8 +182,8 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
error.setDescription(description);
error.setUrl(url());
errors << error;
@@ -340,7 +342,8 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
const QString bindingPropertyName = stringAt(binding->propertyNameIndex);
// Attached property?
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ const QV4::CompiledData::Binding::Type bindingType = binding->type();
+ if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type() : QQmlType();
@@ -373,7 +376,8 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object
QQmlPropertyData * const signalPropertyData = resolver.property(signalName, /*notInRevision ptr*/nullptr);
QQmlPropertyData * const qPropertyData = !qPropertyName.isEmpty() ? resolver.property(qPropertyName) : nullptr;
QString finalSignalHandlerPropertyName = signalName;
- uint flags = QV4::CompiledData::Binding::IsSignalHandlerExpression;
+ QV4::CompiledData::Binding::Flag flag
+ = QV4::CompiledData::Binding::IsSignalHandlerExpression;
const bool isPropertyObserver = !signalPropertyData && qPropertyData && qPropertyData->isBindable();
if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) {
@@ -395,7 +399,7 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object
}
} else if (isPropertyObserver) {
finalSignalHandlerPropertyName = qPropertyName;
- flags = QV4::CompiledData::Binding::IsPropertyObserver;
+ flag = QV4::CompiledData::Binding::IsPropertyObserver;
} else {
if (notInRevision) {
// Try assinging it as a property later
@@ -443,13 +447,13 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object
}
// Binding object to signal means connect the signal to the object's default method.
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject;
+ if (bindingType == QV4::CompiledData::Binding::Type_Object) {
+ binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject);
continue;
}
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType != QV4::CompiledData::Binding::Type_Script) {
+ if (bindingType < QV4::CompiledData::Binding::Type_Script) {
COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)"));
} else {
COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment"));
@@ -457,7 +461,7 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object
}
binding->propertyNameIndex = compiler->registerString(finalSignalHandlerPropertyName);
- binding->flags |= flags;
+ binding->setFlag(flag);
}
return true;
}
@@ -481,12 +485,13 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
QQmlPropertyResolver resolver(propertyCache);
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject
- || binding->flags & QV4::CompiledData::Binding::IsPropertyObserver)
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject
+ || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
continue;
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
const QString propertyName = stringAt(binding->propertyNameIndex);
@@ -508,10 +513,10 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, QStringView, int enumValue, bool)
{
- binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setType(QV4::CompiledData::Binding::Type_Number);
binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
// binding->setNumberValueInternal((double)enumValue);
- binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
+ binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum);
return true;
}
@@ -521,14 +526,24 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
if (!prop->isEnum() && !isIntProp)
return true;
- if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))
- COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex)));
+ if (!prop->isWritable()
+ && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) {
+ COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property")
+ .arg(stringAt(binding->propertyNameIndex)));
+ }
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
if (!string.constData()->isUpper())
return true;
+ // reject any "complex" expression (even simple arithmetic)
+ // we do this by excluding everything that is not part of a
+ // valid identifier or a dot
+ for (const QChar c: string)
+ if (!(c.isLetterOrNumber() || c == u'.' || c == u'_' || c.isSpace()))
+ return true;
+
// we support one or two '.' in the enum phrase:
// * <TypeName>.<EnumValue>
// * <TypeName>.<ScopedEnumName>.<EnumValue>
@@ -664,15 +679,21 @@ void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool
if (!annotateScriptBindings)
annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex);
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Script:
+ if (annotateScriptBindings) {
+ binding->stringIndex = compiler->registerString(
+ compiler->bindingAsString(obj, binding->value.compiledScriptIndex));
+ }
+ break;
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings);
- continue;
- } else if (binding->type != QV4::CompiledData::Binding::Type_Script)
- continue;
- if (!annotateScriptBindings)
- continue;
- const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
- binding->stringIndex = compiler->registerString(script);
+ break;
+ default:
+ break;
+ }
}
}
@@ -701,7 +722,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases()
bool notInRevision = false;
QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
if (pd && pd->isAlias())
- binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
+ binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias);
}
}
}
@@ -728,7 +749,7 @@ void QQmlScriptStringScanner::scan()
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
bool notInRevision = false;
QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
@@ -774,9 +795,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Object)
continue;
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
continue;
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
@@ -848,7 +869,11 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
*syntheticBinding = *binding;
- syntheticBinding->type = QV4::CompiledData::Binding::Type_Object;
+
+ // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
+ syntheticBinding->propertyNameIndex = 0;
+
+ syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
Q_ASSERT(error.isEmpty());
Q_UNUSED(error);
@@ -859,7 +884,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
}
}
-// resolve ignores everything relating to inline components
+// Resolve ignores everything relating to inline components, except for implicit components.
bool QQmlComponentAndAliasResolver::resolve(int root)
{
// Detect real Component {} objects as well as implicitly defined components, such as
@@ -870,28 +895,35 @@ bool QQmlComponentAndAliasResolver::resolve(int root)
const int startObjectIndex = root == 0 ? root : root+1; // root+1, as ic root is handled at the end
for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
QmlIR::Object *obj = qmlObjects->at(i);
- if (root == 0) {
- // normal component root, skip over anything inline component related
- if (obj->isInlineComponent || obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) {
- continue;
- }
- } else {
- if (!(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) ||
- obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- break; // left current inline component (potentially entered a new one)
- }
- QQmlPropertyCache *cache = propertyCaches.at(i);
- if (obj->inheritedTypeNameIndex == 0 && !cache)
- continue;
+ const bool isInlineComponentRoot
+ = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
+ const bool isPartOfInlineComponent
+ = obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent;
+ QQmlPropertyCache *cache = propertyCaches.at(i);
bool isExplicitComponent = false;
-
if (obj->inheritedTypeNameIndex) {
auto *tref = resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(tref);
if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
isExplicitComponent = true;
}
+
+ if (root == 0) {
+ // normal component root, skip over anything inline component related
+ if (isInlineComponentRoot || isPartOfInlineComponent)
+ continue;
+ } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
+ // We've left the current inline component (potentially entered a new one),
+ // but we still need to resolve implicit components which are part of inline components.
+ if (cache && !isExplicitComponent)
+ findAndRegisterImplicitComponents(obj, cache);
+ break;
+ }
+
+ if (obj->inheritedTypeNameIndex == 0 && !cache)
+ continue;
+
if (!isExplicitComponent) {
if (cache)
findAndRegisterImplicitComponents(obj, cache);
@@ -917,7 +949,7 @@ bool QQmlComponentAndAliasResolver::resolve(int root)
COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
}
- if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
+ if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object)
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
// For the root object, we are going to collect ids/aliases and resolve them for as a separate
@@ -985,13 +1017,16 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
return true;
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Object
- && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
- && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
- continue;
-
- if (!collectIdsAndAliases(binding->value.objectIndex))
- return false;
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ if (!collectIdsAndAliases(binding->value.objectIndex))
+ return false;
+ break;
+ default:
+ break;
+ }
}
return true;
@@ -1039,7 +1074,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
recordError(alias->location, tr("Circular alias reference detected"));
return false;
}
@@ -1061,7 +1096,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
bool seenUnresolvedAlias = false;
for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
- if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ if (alias->hasFlag(QV4::CompiledData::Alias::Resolved))
continue;
seenUnresolvedAlias = true;
@@ -1077,8 +1112,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
Q_ASSERT(targetObject->id >= 0);
- alias->targetObjectId = targetObject->id;
- alias->aliasToLocalAlias = false;
+ alias->setTargetObjectId(targetObject->id);
+ alias->setIsAliasToLocalAlias(false);
const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
@@ -1095,7 +1130,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
QQmlPropertyIndex propIdx;
if (property.isEmpty()) {
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
} else {
QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
if (!targetCache) {
@@ -1114,7 +1149,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
bool aliasPointsToOtherAlias = false;
int localAliasIndex = 0;
for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
- if (stringAt(targetAlias->nameIndex) == property) {
+ if (stringAt(targetAlias->nameIndex()) == property) {
aliasPointsToOtherAlias = true;
break;
}
@@ -1122,8 +1157,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (aliasPointsToOtherAlias) {
if (targetObjectIndex == objectIndex) {
alias->localAliasIndex = localAliasIndex;
- alias->aliasToLocalAlias = true;
- alias->flags |= QV4::CompiledData::Alias::Resolved;
+ alias->setIsAliasToLocalAlias(true);
+ alias->setFlag(QV4::CompiledData::Alias::Resolved);
++numResolvedAliases;
continue;
}
@@ -1185,12 +1220,12 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
}
} else {
if (targetProperty->isQObject())
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
}
}
alias->encodedMetaPropertyIndex = propIdx.toEncoded();
- alias->flags |= QV4::CompiledData::Alias::Resolved;
+ alias->setFlag(QV4::CompiledData::Alias::Resolved);
numResolvedAliases++;
}
@@ -1226,7 +1261,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
return scanObject(componentBinding->value.objectIndex);
}
@@ -1264,16 +1299,16 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
continue;
}
} else if (QmlIR::IRBuilder::isSignalPropertyName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
continue;
}
}
@@ -1292,7 +1327,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
bool seenSubObjectWithId = false;
- if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object
+ && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
const bool subObjectValid = scanObject(binding->value.objectIndex);
qSwap(_seenObjectWithId, seenSubObjectWithId);
@@ -1301,22 +1337,24 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
_seenObjectWithId |= seenSubObjectWithId;
}
- if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
+ if (!seenSubObjectWithId && binding->type()
+ != QV4::CompiledData::Binding::Type_GroupProperty
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsDeferredBinding);
obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject
- || binding->flags & QV4::CompiledData::Binding::IsPropertyObserver)
+ const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
+ if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject
+ || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
continue;
if (!pd) {
if (customParser) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
- binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ binding->setFlag(QV4::CompiledData::Binding::IsCustomParserBinding);
}
}
}
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 19ef00ecb2..fe9729cf75 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -203,26 +203,20 @@ bool QQmlTypeData::tryLoadFromDiskCache()
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return false;
}
}
- QQmlType containingType;
- auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
- QTypeRevision version;
- QQmlImportNamespace *ns = nullptr;
- m_importCache.resolveType(containingTypeName, &containingType, &version, &ns);
for (auto&& ic: ics) {
QString const nameString = m_compiledData->stringAt(ic.nameIndex);
- QByteArray const name = nameString.toUtf8();
auto importUrl = finalUrl();
importUrl.setFragment(QString::number(ic.objectIndex));
auto import = new QQmlImportInstance();
- m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType);
+ m_importCache.addInlineComponentImport(import, nameString, importUrl, QQmlType());
}
return true;
@@ -334,8 +328,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = script.script->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column()));
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
errors.prepend(error);
setError(errors);
@@ -358,8 +352,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = type.typeData ? type.typeData->errors() : QList<QQmlError>{};
QQmlError error;
error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
error.setDescription(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(QStringView{typeName}.left(lastDot), type.type.pendingResolutionName()));
errors.prepend(error);
setError(errors);
@@ -374,8 +368,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
@@ -393,8 +387,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
@@ -701,8 +695,8 @@ void QQmlTypeData::continueLoadFromIR()
// resource file system.
QQmlError error = errors.first();
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
setError(error);
return;
}
@@ -727,8 +721,10 @@ void QQmlTypeData::allDependenciesDone()
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
error.setUrl(m_importCache.baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.column()));
errors.prepend(error);
}
}
@@ -858,8 +854,8 @@ void QQmlTypeData::resolveTypes()
bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference;
- if (!resolveType(name, version, ref, unresolvedRef->location.line,
- unresolvedRef->location.column, reportErrors,
+ if (!resolveType(name, version, ref, unresolvedRef->location.line(),
+ unresolvedRef->location.column(), reportErrors,
QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
return;
@@ -878,11 +874,9 @@ void QQmlTypeData::resolveTypes()
}
}
}
- ref.version = version;
-
- ref.location.line = unresolvedRef->location.line;
- ref.location.column = unresolvedRef->location.column;
+ ref.version = version;
+ ref.location = unresolvedRef->location;
ref.needsCreation = unresolvedRef->needsCreation;
m_resolvedTypes.insert(unresolvedRef.key(), ref);
}
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 69331a1cd4..d260700930 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -580,9 +580,14 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
scriptImported(blob, import->location, import->qualifier, QString());
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
+ const QQmlImportDatabase::LocalQmldirSearchLocation searchMode =
+ QQmlMetaType::isStronglyLockedModule(import->uri, import->version)
+ ? QQmlImportDatabase::QmldirCacheOnly
+ : QQmlImportDatabase::QmldirFileAndCache;
+
const QQmlImportDatabase::LocalQmldirResult qmldirResult
= importDatabase->locateLocalQmldir(
- import->uri, import->version,
+ import->uri, import->version, searchMode,
[&](const QString &qmldirFilePath, const QString &qmldirUrl) {
// This is a local library import
const QTypeRevision actualVersion = m_importCache.addLibraryImport(
@@ -740,8 +745,8 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
const QV4::CompiledData::Location importLocation = data->importLocation(this);
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(importLocation.line));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(importLocation.column));
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(importLocation.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(importLocation.column()));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
}
diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
index af97643163..bcc5306cf8 100644
--- a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
+++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
@@ -72,3 +72,5 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
}
QT_END_NAMESPACE
+
+#include "moc_qqmltypeloadernetworkreplyproxy_p.cpp"
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
index 55cd846925..41755767ba 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent_p.h
+++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
@@ -84,6 +84,7 @@ public:
QString preferredPath() const { return m_parser.preferredPath(); }
bool designerSupported() const { return m_parser.designerSupported(); }
+ bool hasTypeInfo() const { return !m_parser.typeInfos().isEmpty(); }
private:
QQmlDirParser m_parser;
diff --git a/src/qml/qml/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp
index 5120728031..c373a4904e 100644
--- a/src/qml/qml/qqmltypemodule.cpp
+++ b/src/qml/qml/qqmltypemodule.cpp
@@ -87,23 +87,8 @@ void QQmlTypeModule::remove(const QQmlTypePrivate *type)
QQmlMetaType::removeQQmlTypePrivate(elementIt.value(), type);
}
-QQmlType QQmlTypeModule::type(const QHashedStringRef &name, QTypeRevision version) const
+QQmlType QQmlTypeModule::findType(const QList<QQmlTypePrivate *> *types, QTypeRevision version)
{
- QMutexLocker lock(&m_mutex);
- QList<QQmlTypePrivate *> *types = m_typeHash.value(name);
- if (types) {
- for (int ii = 0; ii < types->count(); ++ii)
- if (types->at(ii)->version.minorVersion() <= version.minorVersion())
- return QQmlType(types->at(ii));
- }
-
- return QQmlType();
-}
-
-QQmlType QQmlTypeModule::type(const QV4::String *name, QTypeRevision version) const
-{
- QMutexLocker lock(&m_mutex);
- QList<QQmlTypePrivate *> *types = m_typeHash.value(name);
if (types) {
for (int ii = 0; ii < types->count(); ++ii)
if (types->at(ii)->version.minorVersion() <= version.minorVersion())
diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h
index 0ba6245cbb..37b9a32b90 100644
--- a/src/qml/qml/qqmltypemodule_p.h
+++ b/src/qml/qml/qqmltypemodule_p.h
@@ -53,6 +53,7 @@
#include <QtQml/qtqmlglobal.h>
#include <QtQml/private/qstringhash_p.h>
+#include <QtQml/private/qqmltype_p.h>
#include <QtCore/qmutex.h>
#include <QtCore/qstring.h>
#include <QtCore/qversionnumber.h>
@@ -72,6 +73,12 @@ struct String;
class QQmlTypeModule
{
public:
+ enum class LockLevel {
+ Open = 0,
+ Weak = 1,
+ Strong = 2
+ };
+
QQmlTypeModule() = default;
QQmlTypeModule(const QString &uri, quint8 majorVersion)
: m_module(uri), m_majorVersion(majorVersion)
@@ -80,8 +87,17 @@ public:
void add(QQmlTypePrivate *);
void remove(const QQmlTypePrivate *type);
- bool isLocked() const { return m_locked.loadRelaxed() != 0; }
- void lock() { m_locked.storeRelaxed(1); }
+ LockLevel lockLevel() const { return LockLevel(m_lockLevel.loadRelaxed()); }
+ bool setLockLevel(LockLevel mode)
+ {
+ while (true) {
+ const int currentLock = m_lockLevel.loadAcquire();
+ if (currentLock > int(mode))
+ return false;
+ if (currentLock == int(mode) || m_lockLevel.testAndSetRelease(currentLock, int(mode)))
+ return true;
+ }
+ }
QString module() const
{
@@ -99,12 +115,24 @@ public:
quint8 minimumMinorVersion() const { return m_minMinorVersion.loadRelaxed(); }
quint8 maximumMinorVersion() const { return m_maxMinorVersion.loadRelaxed(); }
- QQmlType type(const QHashedStringRef &, QTypeRevision version) const;
- QQmlType type(const QV4::String *, QTypeRevision version) const;
+ QQmlType type(const QHashedStringRef &name, QTypeRevision version) const
+ {
+ QMutexLocker lock(&m_mutex);
+ return findType(m_typeHash.value(name), version);
+ }
+
+ QQmlType type(const QV4::String *name, QTypeRevision version) const
+ {
+ QMutexLocker lock(&m_mutex);
+ return findType(m_typeHash.value(name), version);
+ }
void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
private:
+ static Q_QML_PRIVATE_EXPORT QQmlType findType(
+ const QList<QQmlTypePrivate *> *types, QTypeRevision version);
+
const QString m_module;
const quint8 m_majorVersion = 0;
@@ -114,8 +142,8 @@ private:
// Can only ever increase
QAtomicInt m_maxMinorVersion = 0;
- // Bool. Can only be set to 1 once.
- QAtomicInt m_locked = 0;
+ // LockLevel. Can only be increased.
+ QAtomicInt m_lockLevel = int(LockLevel::Open);
using TypeHash = QStringHash<QList<QQmlTypePrivate *>>;
TypeHash m_typeHash;
diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp
index 45333668e3..0f23b3190e 100644
--- a/src/qml/qml/qqmltypenamecache.cpp
+++ b/src/qml/qml/qqmltypenamecache.cpp
@@ -86,119 +86,5 @@ void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex,
m_namedImports.insert(name, import);
}
-QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) const
-{
- Result result = query(m_namedImports, name);
-
- if (!result.isValid())
- result = typeSearch(m_anonymousImports, name);
-
- if (!result.isValid())
- result = query(m_anonymousCompositeSingletons, name);
-
- if (!result.isValid()) {
- // Look up anonymous types from the imports of this document
- QQmlImportNamespace *typeNamespace = nullptr;
- QList<QQmlError> errors;
- QQmlType t;
- bool typeFound = m_imports.resolveType(name, &t, nullptr, &typeNamespace, &errors);
- if (typeFound) {
- return Result(t);
- }
-
- }
-
- return result;
-}
-
-QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name,
- const QQmlImportRef *importNamespace) const
-{
- Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
-
- Result result = typeSearch(importNamespace->modules, name);
-
- if (!result.isValid())
- result = query(importNamespace->compositeSingletons, name);
-
- if (!result.isValid()) {
- // Look up types from the imports of this document
- // ### it would be nice if QQmlImports allowed us to resolve a namespace
- // first, and then types on it.
- QString qualifiedTypeName = importNamespace->m_qualifier + QLatin1Char('.') + name.toString();
- QQmlImportNamespace *typeNamespace = nullptr;
- QList<QQmlError> errors;
- QQmlType t;
- bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
- if (typeFound) {
- return Result(t);
- }
- }
-
- return result;
-}
-
-QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, QQmlImport::RecursionRestriction recursionRestriction) const
-{
- Result result = query(m_namedImports, name);
-
- if (!result.isValid())
- result = typeSearch(m_anonymousImports, name);
-
- if (!result.isValid())
- result = query(m_anonymousCompositeSingletons, name);
-
- if (!result.isValid()) {
- // Look up anonymous types from the imports of this document
- QString typeName = name->toQStringNoThrow();
- QQmlImportNamespace *typeNamespace = nullptr;
- QList<QQmlError> errors;
- QQmlType t;
- bool typeRecursionDetected = false;
- bool typeFound = m_imports.resolveType(typeName, &t, nullptr, &typeNamespace, &errors,
- QQmlType::AnyRegistrationType,
- recursionRestriction == QQmlImport::AllowRecursion ? &typeRecursionDetected : nullptr);
- if (typeFound) {
- return Result(t);
- }
-
- }
-
- return result;
-}
-
-QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, const QQmlImportRef *importNamespace) const
-{
- Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
-
- QMap<const QQmlImportRef *, QStringHash<QQmlImportRef> >::const_iterator it = m_namespacedImports.constFind(importNamespace);
- if (it != m_namespacedImports.constEnd()) {
- Result r = query(*it, name);
- if (r.isValid())
- return r;
- }
-
- Result r = typeSearch(importNamespace->modules, name);
-
- if (!r.isValid())
- r = query(importNamespace->compositeSingletons, name);
-
- if (!r.isValid()) {
- // Look up types from the imports of this document
- // ### it would be nice if QQmlImports allowed us to resolve a namespace
- // first, and then types on it.
- QString qualifiedTypeName = importNamespace->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow();
- QQmlImportNamespace *typeNamespace = nullptr;
- QList<QQmlError> errors;
- QQmlType t;
- bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
- if (typeFound) {
- return Result(t);
- }
- }
-
- return r;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h
index 974f99ecae..5e1a88b2a5 100644
--- a/src/qml/qml/qqmltypenamecache_p.h
+++ b/src/qml/qml/qqmltypenamecache_p.h
@@ -104,16 +104,131 @@ public:
const QQmlImportRef *importNamespace;
int scriptIndex;
};
- Result query(const QHashedStringRef &) const;
- Result query(const QHashedStringRef &, const QQmlImportRef *importNamespace) const;
- Result query(const QV4::String *, QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const;
- Result query(const QV4::String *, const QQmlImportRef *importNamespace) const;
+
+ enum class QueryNamespaced { No, Yes };
+
+ // Restrict the types allowed for key. We don't want QV4::ScopedString, for example.
+
+ template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
+ Result query(const QHashedStringRef &key) const
+ {
+ return doQuery<const QHashedStringRef &, recursionRestriction>(key);
+ }
+
+ template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
+ Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace) const
+ {
+ return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace);
+ }
+
+ template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
+ Result query(const QV4::String *key) const
+ {
+ return doQuery<const QV4::String *, recursionRestriction>(key);
+ }
+
+ template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
+ Result query(const QV4::String *key, const QQmlImportRef *importNamespace) const
+ {
+ return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace);
+ }
private:
friend class QQmlImports;
+ static QHashedStringRef toHashedStringRef(const QHashedStringRef &key) { return key; }
+ static QHashedStringRef toHashedStringRef(const QV4::String *key)
+ {
+ const QV4::Heap::String *heapString = key->d();
+
+ // toQString() would also do simplifyString(). Therefore, we can be sure that this
+ // is safe. Any other operation on the string data cannot keep references on the
+ // non-simplified pieces.
+ if (heapString->subtype >= QV4::Heap::String::StringType_Complex)
+ heapString->simplifyString();
+
+ // This is safe because the string data is backed by the QV4::String we got as
+ // parameter. The contract about passing V4 values as parameters is that you have to
+ // scope them first, so that they don't get gc'd while the callee is working on them.
+ const QStringPrivate &text = heapString->text();
+ return QHashedStringRef(QStringView(text.ptr, text.size));
+ }
+
+ static QString toQString(const QHashedStringRef &key) { return key.toString(); }
+ static QString toQString(const QV4::String *key) { return key->toQStringNoThrow(); }
+
+ template<typename Key, QQmlImport::RecursionRestriction recursionRestriction>
+ Result doQuery(Key name) const
+ {
+ Result result = doQuery(m_namedImports, name);
+
+ if (!result.isValid())
+ result = typeSearch(m_anonymousImports, name);
+
+ if (!result.isValid())
+ result = doQuery(m_anonymousCompositeSingletons, name);
+
+ if (!result.isValid()) {
+ // Look up anonymous types from the imports of this document
+ // ### it would be nice if QQmlImports allowed us to resolve a namespace
+ // first, and then types on it.
+ QQmlImportNamespace *typeNamespace = nullptr;
+ QList<QQmlError> errors;
+ QQmlType t;
+ bool typeRecursionDetected = false;
+ const bool typeFound = m_imports.resolveType(
+ toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
+ QQmlType::AnyRegistrationType,
+ recursionRestriction == QQmlImport::AllowRecursion
+ ? &typeRecursionDetected
+ : nullptr);
+ if (typeFound)
+ return Result(t);
+
+ }
+
+ return result;
+ }
+
+ template<typename Key, QueryNamespaced queryNamespaced>
+ Result doQuery(Key name, const QQmlImportRef *importNamespace) const
+ {
+ Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
+
+ if constexpr (queryNamespaced == QueryNamespaced::Yes) {
+ QMap<const QQmlImportRef *, QStringHash<QQmlImportRef> >::const_iterator it
+ = m_namespacedImports.constFind(importNamespace);
+ if (it != m_namespacedImports.constEnd()) {
+ Result r = doQuery(*it, name);
+ if (r.isValid())
+ return r;
+ }
+ }
+
+ Result result = typeSearch(importNamespace->modules, name);
+
+ if (!result.isValid())
+ result = doQuery(importNamespace->compositeSingletons, name);
+
+ if (!result.isValid()) {
+ // Look up types from the imports of this document
+ // ### it would be nice if QQmlImports allowed us to resolve a namespace
+ // first, and then types on it.
+ const QString qualifiedTypeName = importNamespace->m_qualifier + u'.' + toQString(name);
+ QQmlImportNamespace *typeNamespace = nullptr;
+ QList<QQmlError> errors;
+ QQmlType t;
+ bool typeFound = m_imports.resolveType(
+ qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
+ if (typeFound)
+ return Result(t);
+ }
+
+ return result;
+ }
+
template<typename Key>
- Result query(const QStringHash<QQmlImportRef> &imports, Key key) const
+ Result doQuery(const QStringHash<QQmlImportRef> &imports, Key key) const
{
QQmlImportRef *i = imports.value(key);
if (i) {
@@ -129,7 +244,7 @@ private:
}
template<typename Key>
- Result query(const QStringHash<QUrl> &urls, Key key) const
+ Result doQuery(const QStringHash<QUrl> &urls, Key key) const
{
QUrl *url = urls.value(key);
if (url) {
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index c4d2d82a50..0580af56f0 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -111,11 +111,14 @@ QObject* QQmlTypeWrapper::singletonObject() const
QVariant QQmlTypeWrapper::toVariant() const
{
- if (!isSingleton())
- return QVariant::fromValue<QObject *>(d()->object);
-
QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
const QQmlType type = d()->type();
+
+ if (!isSingleton()) {
+ return QVariant::fromValue(qmlAttachedPropertiesObject(
+ d()->object, type.attachedPropertiesFunction(e)));
+ }
+
if (type.isQJSValueSingleton())
return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type));
@@ -411,8 +414,10 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return Encode(false);
QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
- ExecutableCompilationUnit *cu = td->compilationUnit();
- myQmlType = qenginepriv->metaObjectForType(cu->typeIds.id.id());
+ if (ExecutableCompilationUnit *cu = td->compilationUnit())
+ myQmlType = qenginepriv->metaObjectForType(cu->typeIds.id.id());
+ else
+ return Encode(false); // It seems myQmlType has some errors, so we could not compile it.
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId.id());
}
@@ -450,11 +455,8 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
if (property) {
ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
- lookup->qobjectLookup.qmlTypeIc = This->internalClass();
- lookup->qobjectLookup.ic = val->objectValue()->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
+ setupQObjectLookup(lookup, ddata, property,
+ val->objectValue(), This);
lookup->getter = QQmlTypeWrapper::lookupSingletonProperty;
return lookup->getter(lookup, engine, *object);
}
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index c2bbd7f498..2cba0375e7 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -281,10 +281,27 @@ PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m,
{
if (id.isString()) {
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
- QQmlPropertyData result = r->dataForPropertyKey(id);
- if (p && result.isValid())
- p->value = getGadgetProperty(r->engine(), r->d(), result.propType(), result.coreIndex(), result.isFunction(), result.isEnum());
- return result.isValid() ? Attr_Data : Attr_Invalid;
+ Q_ASSERT(r);
+
+ const QQmlPropertyData result = r->dataForPropertyKey(id);
+ if (!result.isValid())
+ return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it.
+
+ if (!p)
+ return Attr_Data; // Property exists, but we're not interested in the value
+
+ const QQmlValueTypeReference *ref = r->as<const QQmlValueTypeReference>();
+ if (!ref || ref->readReferenceValue()) {
+ // Property exists, and we can retrieve it
+ p->value = getGadgetProperty(
+ r->engine(), r->d(), result.propType(), result.coreIndex(),
+ result.isFunction(), result.isEnum());
+ } else {
+ // Property exists, but we can't retrieve it. Make it undefined.
+ p->value = Encode::undefined();
+ }
+
+ return Attr_Data;
}
return QV4::Object::virtualGetOwnProperty(m, id, p);
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index e7248b038a..b6f68536ca 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -236,17 +236,18 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId];
if (!aliasData->isObjectAlias()) {
QQmlRefPointer<QQmlContextData> ctxt = metaObject->ctxt;
- QObject *target = ctxt->idValue(aliasData->targetObjectId);
+ QObject *target = ctxt->idValue(aliasData->targetObjectId());
if (!target)
return;
- QQmlData *targetDData = QQmlData::get(target, /*create*/false);
- if (!targetDData)
- return;
QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
int coreIndex = encodedIndex.coreIndex();
int valueTypeIndex = encodedIndex.valueTypeIndex();
- const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
+ QJSEngine *engine = qjsEngine(target);
+ if (!engine)
+ return; // dont crash
+ const QQmlPropertyData *pd =
+ QQmlData::ensurePropertyCache(engine, target)->property(coreIndex);
if (pd && valueTypeIndex != -1 && !QQmlMetaType::valueType(pd->propType())) {
// deep alias
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine());
@@ -767,7 +768,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
break;
case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
+ if (property.isList()) {
// when reading from the list, we need to find the correct MetaObject,
// namely this. However, obejct->metaObject might point to any MetaObject
// down the inheritance hierarchy, so we need to store how far we have
@@ -872,7 +873,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
break;
case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
+ if (property.isList()) {
// Writing such a property is not supported. Content is added through the list property
// methods.
} else {
@@ -894,16 +895,18 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (id < aliasCount) {
const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id];
- if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty)
- *reinterpret_cast<void **>(a[0]) = nullptr;
+ if (aliasData->hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject)
+ && c == QMetaObject::ReadProperty){
+ *reinterpret_cast<void **>(a[0]) = nullptr;
+ }
if (ctxt.isNull())
return -1;
- while (aliasData->aliasToLocalAlias)
+ while (aliasData->isAliasToLocalAlias())
aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
- QObject *target = ctxt->idValue(aliasData->targetObjectId);
+ QObject *target = ctxt->idValue(aliasData->targetObjectId());
if (!target)
return -1;
@@ -1006,7 +1009,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr;
if (arguments && arguments->names) {
- const auto parameterCount = arguments->names->count();
+ const quint32 parameterCount = arguments->names->count();
Q_ASSERT(parameterCount == function->formalParameterCount());
if (void *result = a[0])
arguments->types[0].destruct(result);
@@ -1250,9 +1253,9 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex,
const int aliasId = index - propOffset() - compiledObject->nProperties;
const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
- while (aliasData->aliasToLocalAlias)
+ while (aliasData->isAliasToLocalAlias())
aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
- *target = ctxt->idValue(aliasData->targetObjectId);
+ *target = ctxt->idValue(aliasData->targetObjectId());
if (!*target)
return false;
@@ -1280,7 +1283,7 @@ void QQmlVMEMetaObject::connectAlias(int aliasId)
}
endpoint->metaObject = this;
- endpoint->connect(ctxt->idValueBindings(aliasData->targetObjectId));
+ endpoint->connect(ctxt->idValueBindings(aliasData->targetObjectId()));
endpoint->tryConnect();
}
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index f15c3aa201..46dc108b9e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -375,26 +375,10 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
}
/*!
- \qmlmethod matrix4x4 Qt::matrix4x4(real m11, real m12, real m13, real m14, real m21, real m22, real m23, real m24, real m31, real m32, real m33, real m34, real m41, real m42, real m43, real m44)
-
- Returns a matrix4x4 with the specified values.
-
- The arguments correspond to their positions in the matrix:
+ \qmlmethod matrix4x4 Qt::matrix4x4()
- \table
- \row \li \a m11 \li \a m12 \li \a m13 \li \a m14
- \row \li \a m21 \li \a m22 \li \a m23 \li \a m24
- \row \li \a m31 \li \a m32 \li \a m33 \li \a m34
- \row \li \a m41 \li \a m42 \li \a m43 \li \a m44
- \endtable
-
- Alternatively, the function may be called with a single argument
- where that argument is a JavaScript array which contains the sixteen
- matrix values.
-
- Finally, the function may be called with no arguments and the resulting
- matrix will be the identity matrix.
-*/
+ Returns an identity matrix4x4.
+ */
QVariant QtObject::matrix4x4() const
{
QVariant variant;
@@ -402,6 +386,21 @@ QVariant QtObject::matrix4x4() const
return variant;
}
+/*!
+ \qmlmethod matrix4x4 Qt::matrix4x4(var values)
+
+ Returns a matrix4x4 with the specified \a values. \a values is expected to
+ be a JavaScript array with 16 entries.
+
+ The array indices correspond to positions in the matrix as follows:
+
+ \table
+ \row \li 0 \li 1 \li 2 \li 3
+ \row \li 4 \li 5 \li 6 \li 7
+ \row \li 8 \li 9 \li 10 \li 11
+ \row \li 12 \li 13 \li 14 \li 15
+ \endtable
+*/
QVariant QtObject::matrix4x4(const QJSValue &value) const
{
if (value.isObject()) {
@@ -415,6 +414,20 @@ QVariant QtObject::matrix4x4(const QJSValue &value) const
return QVariant();
}
+/*!
+ \qmlmethod matrix4x4 Qt::matrix4x4(real m11, real m12, real m13, real m14, real m21, real m22, real m23, real m24, real m31, real m32, real m33, real m34, real m41, real m42, real m43, real m44)
+
+ Returns a matrix4x4 with the specified values.
+
+ The arguments correspond to their positions in the matrix:
+
+ \table
+ \row \li \a m11 \li \a m12 \li \a m13 \li \a m14
+ \row \li \a m21 \li \a m22 \li \a m23 \li \a m24
+ \row \li \a m31 \li \a m32 \li \a m33 \li \a m34
+ \row \li \a m41 \li \a m42 \li \a m43 \li \a m44
+ \endtable
+*/
QVariant QtObject::matrix4x4(double m11, double m12, double m13, double m14,
double m21, double m22, double m23, double m24,
double m31, double m32, double m33, double m34,
@@ -811,9 +824,30 @@ bool QtObject::openUrlExternally(const QUrl &url) const
}
/*!
+ \qmlmethod url Qt::url(url url)
+
+ Returns \a url verbatim. This can be used to force a type coercion to \c url.
+ In contrast to Qt.resolvedUrl() this retains any relative URLs. As strings
+ are implicitly converted to urls, the function can be called with a string
+ as argument, and will then return a url.
+
+ \sa resolvedUrl()
+*/
+QUrl QtObject::url(const QUrl &url) const
+{
+ return url;
+}
+
+/*!
\qmlmethod url Qt::resolvedUrl(url url)
Returns \a url resolved relative to the URL of the caller.
+
+ If there is no caller or the caller is not associated with a QML context,
+ returns \a url resolved relative to the QML engine's base URL. If the QML
+ engine has no base URL, just returns \a url.
+
+ \sa url()
*/
QUrl QtObject::resolvedUrl(const QUrl &url) const
{
@@ -825,6 +859,29 @@ QUrl QtObject::resolvedUrl(const QUrl &url) const
}
/*!
+ \qmlmethod url Qt::resolvedUrl(url url, object context)
+
+ Returns \a url resolved relative to the URL of the QML context of
+ \a context. If \a context is not associated with a QML context,
+ returns \a url resolved relative to the QML engine's base URL. If
+ the QML engine has no base URL, just returns \a url.
+
+ \sa url()
+*/
+QUrl QtObject::resolvedUrl(const QUrl &url, QObject *context) const
+{
+ if (context) {
+ QQmlData *data = QQmlData::get(context);
+ if (data && data->outerContext)
+ return data->outerContext->resolvedUrl(url);
+ }
+
+ if (QQmlEngine *engine = qmlEngine())
+ return engine->baseUrl().resolved(url);
+ return url;
+}
+
+/*!
\qmlmethod list<string> Qt::fontFamilies()
Returns a list of the font families available to the application.
@@ -1031,7 +1088,7 @@ QObject *QtObject::createQmlObject(const QString &qml, QObject *parent, const QU
}
/*!
-\qmlmethod object Qt::createComponent(url, mode, parent)
+\qmlmethod Component Qt::createComponent(url url, enumeration mode, QtObject parent)
Returns a \l Component object created using the QML file at the specified \a url,
or \c null if an empty string was given.
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index 972a0d0b31..7f40a34ddd 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -144,7 +144,9 @@ public:
Q_INVOKABLE QLocale locale(const QString &name) const;
#endif
+ Q_INVOKABLE QUrl url(const QUrl &url) const;
Q_INVOKABLE QUrl resolvedUrl(const QUrl &url) const;
+ Q_INVOKABLE QUrl resolvedUrl(const QUrl &url, QObject *context) const;
Q_INVOKABLE bool openUrlExternally(const QUrl &url) const;
Q_INVOKABLE QVariant font(const QJSValue &fontSpecifier) const;
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index 1dbf35f1d3..ae6edde3c0 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -85,6 +85,7 @@ void QQmlDirParser::clear()
_designerSupported = false;
_typeInfos.clear();
_classNames.clear();
+ _linkTarget.clear();
}
inline static void scanSpace(const QChar *&ch) {
@@ -321,6 +322,24 @@ bool QQmlDirParser::parse(const QString &source)
}
_preferredPath = sections[1];
+ } else if (sections[0] == QLatin1String("linktarget")) {
+ if (sectionCount < 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("linktarget directive requires an argument, "
+ "but %1 were provided")
+ .arg(sectionCount - 1));
+ continue;
+ }
+
+ if (!_linkTarget.isEmpty()) {
+ reportError(
+ lineNumber, 0,
+ QStringLiteral(
+ "only one linktarget directive may be defined in a qmldir file"));
+ continue;
+ }
+
+ _linkTarget = sections[1];
} else if (sectionCount == 2) {
// No version specified (should only be used for relative qmldir files)
const Component entry(sections[0], sections[1], QTypeRevision());
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index eabaceae91..22073d26c5 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -161,6 +161,7 @@ public:
QStringList typeInfos() const;
QStringList classNames() const;
QString preferredPath() const;
+ QString linkTarget() const { return _linkTarget; }
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
@@ -178,6 +179,7 @@ private:
bool _designerSupported = false;
QStringList _typeInfos;
QStringList _classNames;
+ QString _linkTarget;
};
using QQmlDirComponents = QMultiHash<QString,QQmlDirParser::Component>;
diff --git a/src/qml/qmldirparser/qqmlimportresolver_p.h b/src/qml/qmldirparser/qqmlimportresolver_p.h
index c2f49ee4ec..743e9f5526 100644
--- a/src/qml/qmldirparser/qqmlimportresolver_p.h
+++ b/src/qml/qmldirparser/qqmlimportresolver_p.h
@@ -51,13 +51,15 @@
// We mean it.
//
+#include <private/qtqmlcompilerglobal_p.h>
+
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qversionnumber.h>
QT_BEGIN_NAMESPACE
-QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths,
+Q_QMLCOMPILER_PRIVATE_EXPORT QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths,
QTypeRevision version);
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index 8e74711776..17e3e88f9b 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -368,12 +368,8 @@ void QQmlBind::setDelayed(bool delayed)
The default value is \c Binding.RestoreBindingOrValue.
- If you rely on any specific behavior regarding the restoration of plain
- values when bindings get disabled you should migrate to explicitly set the
- restoreMode.
-
- Reliance on a restoreMode that doesn't restore the previous binding or value
- for a specific property results in a run-time warning.
+ \note This property exists for backwards compatibility with earlier versions
+ of Qt. Don't use it in new code.
*/
QQmlBind::RestorationMode QQmlBind::restoreMode() const
{
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index 7767909169..70e048b0ea 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -60,15 +60,13 @@ Q_LOGGING_CATEGORY(lcQmlConnections, "qt.qml.connections")
class QQmlConnectionsPrivate : public QObjectPrivate
{
public:
- QQmlConnectionsPrivate() : target(nullptr), enabled(true), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
-
QList<QQmlBoundSignal*> boundsignals;
- QObject *target;
+ QQmlGuard<QObject> target;
- bool enabled;
- bool targetSet;
- bool ignoreUnknownSignals;
- bool componentcomplete;
+ bool enabled = true;
+ bool targetSet = false;
+ bool ignoreUnknownSignals = false;
+ bool componentcomplete = true;
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding *> bindings;
@@ -130,6 +128,12 @@ public:
}
\endqml
+ \note For backwards compatibility you can also specify the signal handlers
+ without \c{function}, like you would specify them directly in the target
+ object. This is not recommended. If you specify one signal handler this way,
+ then all signal handlers specified as \c{function} in the same Connections
+ object are ignored.
+
\sa {Qt QML}
*/
QQmlConnections::QQmlConnections(QObject *parent) :
@@ -153,7 +157,7 @@ QQmlConnections::~QQmlConnections()
QObject *QQmlConnections::target() const
{
Q_D(const QQmlConnections);
- return d->targetSet ? d->target : parent();
+ return d->targetSet ? d->target.data() : parent();
}
class QQmlBoundSignalDeleter : public QObject
@@ -248,17 +252,20 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableC
return;
}
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Script)
+ continue;
+
+ if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex);
if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
error(binding, QQmlConnections::tr("Connections: nested objects not allowed"));
else
error(binding, QQmlConnections::tr("Connections: syntax error"));
return;
- } if (binding->type != QV4::CompiledData::Binding::Type_Script) {
- error(binding, QQmlConnections::tr("Connections: script expected"));
- return;
}
+
+ error(binding, QQmlConnections::tr("Connections: script expected"));
+ return;
}
}
@@ -353,7 +360,7 @@ void QQmlConnections::connectSignalsToBindings()
QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext : nullptr;
for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex);
QQmlProperty prop(target, propName);
diff --git a/src/qml/types/qqmlmodelindexvaluetype_p.h b/src/qml/types/qqmlmodelindexvaluetype_p.h
deleted file mode 100644
index 2c37d91c71..0000000000
--- a/src/qml/types/qqmlmodelindexvaluetype_p.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QQMLMODELINDEXVALUETYPE_P_H
-#define QQMLMODELINDEXVALUETYPE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qabstractitemmodel.h>
-#include <QtCore/qitemselectionmodel.h>
-#include <QtQml/qqml.h>
-
-QT_BEGIN_NAMESPACE
-
-struct QQmlModelIndexValueType
-{
- QModelIndex v;
-
- Q_PROPERTY(int row READ row CONSTANT FINAL)
- Q_PROPERTY(int column READ column CONSTANT FINAL)
- Q_PROPERTY(QModelIndex parent READ parent FINAL)
- Q_PROPERTY(bool valid READ isValid CONSTANT FINAL)
- Q_PROPERTY(QAbstractItemModel *model READ model CONSTANT FINAL)
- Q_PROPERTY(quint64 internalId READ internalId CONSTANT FINAL)
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED(QQmlModelIndexValueType)
- QML_FOREIGN(QModelIndex)
- QML_ADDED_IN_VERSION(2, 0)
-
-public:
- Q_INVOKABLE QString toString() const
- { return QLatin1String("QModelIndex") + propertiesString(v); }
-
- inline int row() const noexcept { return v.row(); }
- inline int column() const noexcept { return v.column(); }
- inline QModelIndex parent() const { return v.parent(); }
- inline bool isValid() const noexcept { return v.isValid(); }
- inline QAbstractItemModel *model() const noexcept
- { return const_cast<QAbstractItemModel *>(v.model()); }
- quint64 internalId() const { return v.internalId(); }
-
- static QString propertiesString(const QModelIndex &idx);
-
- static QPersistentModelIndex toPersistentModelIndex(const QModelIndex &index)
- { return QPersistentModelIndex(index); }
-};
-
-struct QQmlPersistentModelIndexValueType
-{
- QPersistentModelIndex v;
-
- Q_PROPERTY(int row READ row FINAL)
- Q_PROPERTY(int column READ column FINAL)
- Q_PROPERTY(QModelIndex parent READ parent FINAL)
- Q_PROPERTY(bool valid READ isValid FINAL)
- Q_PROPERTY(QAbstractItemModel *model READ model FINAL)
- Q_PROPERTY(quint64 internalId READ internalId FINAL)
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED(QQmlPersistentModelIndexValueType)
- QML_FOREIGN(QPersistentModelIndex)
- QML_ADDED_IN_VERSION(2, 0)
-
-public:
- Q_INVOKABLE QString toString() const
- { return QLatin1String("QPersistentModelIndex") + QQmlModelIndexValueType::propertiesString(v); }
-
- inline int row() const { return v.row(); }
- inline int column() const { return v.column(); }
- inline QModelIndex parent() const { return v.parent(); }
- inline bool isValid() const { return v.isValid(); }
- inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); }
- inline quint64 internalId() const { return v.internalId(); }
-};
-
-struct QQmlItemSelectionRangeValueType
-{
- QItemSelectionRange v;
-
- Q_PROPERTY(int top READ top FINAL)
- Q_PROPERTY(int left READ left FINAL)
- Q_PROPERTY(int bottom READ bottom FINAL)
- Q_PROPERTY(int right READ right FINAL)
- Q_PROPERTY(int width READ width FINAL)
- Q_PROPERTY(int height READ height FINAL)
- Q_PROPERTY(QPersistentModelIndex topLeft READ topLeft FINAL)
- Q_PROPERTY(QPersistentModelIndex bottomRight READ bottomRight FINAL)
- Q_PROPERTY(QModelIndex parent READ parent FINAL)
- Q_PROPERTY(bool valid READ isValid FINAL)
- Q_PROPERTY(bool empty READ isEmpty FINAL)
- Q_PROPERTY(QAbstractItemModel *model READ model FINAL)
- Q_GADGET
- QML_ANONYMOUS
- QML_EXTENDED(QQmlItemSelectionRangeValueType)
- QML_FOREIGN(QItemSelectionRange)
- QML_ADDED_IN_VERSION(2, 0)
-
-public:
- Q_INVOKABLE QString toString() const;
- Q_INVOKABLE inline bool contains(const QModelIndex &index) const
- { return v.contains(index); }
- Q_INVOKABLE inline bool contains(int row, int column, const QModelIndex &parentIndex) const
- { return v.contains(row, column, parentIndex); }
- Q_INVOKABLE inline bool intersects(const QItemSelectionRange &other) const
- { return v.intersects(other); }
- Q_INVOKABLE QItemSelectionRange intersected(const QItemSelectionRange &other) const
- { return v.intersected(other); }
-
- inline int top() const { return v.top(); }
- inline int left() const { return v.left(); }
- inline int bottom() const { return v.bottom(); }
- inline int right() const { return v.right(); }
- inline int width() const { return v.width(); }
- inline int height() const { return v.height(); }
- inline QPersistentModelIndex &topLeft() const { return const_cast<QPersistentModelIndex &>(v.topLeft()); }
- inline QPersistentModelIndex &bottomRight() const { return const_cast<QPersistentModelIndex &>(v.bottomRight()); }
- inline QModelIndex parent() const { return v.parent(); }
- inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); }
- inline bool isValid() const { return v.isValid(); }
- inline bool isEmpty() const { return v.isEmpty(); }
-};
-
-#undef QLISTVALUETYPE_INVOKABLE_API
-
-QT_END_NAMESPACE
-
-#endif // QQMLMODELINDEXVALUETYPE_P_H
-
diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp
index 2bf8ab0190..23bf1b307c 100644
--- a/src/qml/util/qqmlpropertymap.cpp
+++ b/src/qml/util/qqmlpropertymap.cpp
@@ -409,3 +409,5 @@ QQmlPropertyMap::QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *p
*/
QT_END_NAMESPACE
+
+#include "moc_qqmlpropertymap.cpp"