diff options
Diffstat (limited to 'src/qml')
58 files changed, 1083 insertions, 581 deletions
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index d50d66f801..dd76f8496b 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -2399,7 +2399,7 @@ but this file does not exist. Possible reasons include: ") endif() - # Find QML import paths. + # 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}") @@ -2407,7 +2407,9 @@ but this file does not exist. Possible reasons include: # We have multiple installation prefixes: one per Qt repository (conan). Add those that have # a "qml" subdirectory. set(qml_import_paths) - foreach(root IN ITEMS "${QT6_INSTALL_PREFIX};${_qt_additional_packages_prefix_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}") @@ -2415,11 +2417,6 @@ but this file does not exist. Possible reasons include: endforeach() endif() - # Construct the -importPath arguments. - set(import_path_arguments) - foreach(path IN LISTS qml_import_paths) - list(APPEND import_path_arguments -importPath ${path}) - endforeach() # Run qmlimportscanner to generate the cmake file that records the import entries get_target_property(target_source_dir ${target} SOURCE_DIR) @@ -2433,7 +2430,6 @@ but this file does not exist. Possible reasons include: -rootPath "${target_source_dir}" -cmake-output -output-file "${imports_file}" - ${import_path_arguments} ) get_target_property(qml_import_path ${target} QT_QML_IMPORT_PATH) @@ -2444,18 +2440,26 @@ but this file does not exist. Possible reasons include: # Facilitate self-import so we can find the qmldir file get_target_property(module_out_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY) if(module_out_dir) - list(APPEND cmd_args "${module_out_dir}") + list(APPEND qml_import_paths "${module_out_dir}") endif() # Find qmldir files we copied to the build directory if(NOT "${QT_QML_OUTPUT_DIRECTORY}" STREQUAL "") if(EXISTS "${QT_QML_OUTPUT_DIRECTORY}") - list(APPEND cmd_args "${QT_QML_OUTPUT_DIRECTORY}") + list(APPEND qml_import_paths "${QT_QML_OUTPUT_DIRECTORY}") endif() else() - list(APPEND cmd_args "${CMAKE_CURRENT_BINARY_DIR}") + 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 diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h index 2517442bb6..0e233747ff 100644 --- a/src/qml/common/qjsnumbercoercion.h +++ b/src/qml/common/qjsnumbercoercion.h @@ -48,6 +48,10 @@ QT_BEGIN_NAMESPACE class QJSNumberCoercion { public: + static constexpr bool isInteger(double d) { + return equals(d, d) && equals(static_cast<int>(d), d); + } + static constexpr int toInteger(double d) { if (!equals(d, d)) return 0; diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index b14e523ab2..35119364d9 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -129,7 +129,7 @@ struct TableIterator struct Location { Location() : m_data(QSpecialIntegerBitfieldZero) {} - Location(quint32 l, quint32 c) + Location(quint32 l, quint32 c) : Location() { m_data.set<LineField>(l); m_data.set<ColumnField>(c); @@ -182,7 +182,7 @@ struct RegExp }; RegExp() : m_data(QSpecialIntegerBitfieldZero) {} - RegExp(quint32 flags, quint32 stringIndex) + RegExp(quint32 flags, quint32 stringIndex) : RegExp() { m_data.set<FlagsField>(flags); m_data.set<StringIndexField>(stringIndex); @@ -211,7 +211,7 @@ struct Lookup quint32 nameIndex() const { return m_data.get<NameIndexField>(); } Lookup() : m_data(QSpecialIntegerBitfieldZero) {} - Lookup(Type type, quint32 nameIndex) + Lookup(Type type, quint32 nameIndex) : Lookup() { m_data.set<TypeAndFlagsField>(type); m_data.set<NameIndexField>(nameIndex); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 0f1ae70bfa..7acc3b14b2 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -306,7 +306,7 @@ struct Function Function *next; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression +struct Q_QML_COMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression { CompiledFunctionOrExpression() {} @@ -317,7 +317,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression CompiledFunctionOrExpression *next = nullptr; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Object +struct Q_QML_COMPILER_PRIVATE_EXPORT Object { Q_DECLARE_TR_FUNCTIONS(Object) public: @@ -410,7 +410,7 @@ private: PoolList<RequiredPropertyExtraData> *requiredPropertyExtraDatas; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma +struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma { enum PragmaType { @@ -434,7 +434,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma QV4::CompiledData::Location location; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Document +struct Q_QML_COMPILER_PRIVATE_EXPORT Document { Document(bool debugMode); QString code; @@ -455,7 +455,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT Document Object* objectAt(int i) const {return objects.at(i);} }; -class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives +class Q_QML_COMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives { QmlIR::Document *document; QQmlJS::Engine *engine; @@ -469,7 +469,7 @@ public: void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor +struct Q_QML_COMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor { Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) public: @@ -591,7 +591,7 @@ public: bool insideInlineComponent = false; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator +struct Q_QML_COMPILER_PRIVATE_EXPORT QmlUnitGenerator { void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher()); @@ -600,7 +600,7 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen +struct Q_QML_COMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { JSCodeGen(Document *document, const QSet<QString> &globalNames, QV4::Compiler::CodegenWarningInterface *iface = diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index c1526e58a1..b9c470320d 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -92,7 +92,7 @@ namespace Moth { #define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) -class Q_QMLCOMPILER_PRIVATE_EXPORT ByteCodeHandler +class Q_QML_COMPILER_PRIVATE_EXPORT ByteCodeHandler { Q_DISABLE_COPY_MOVE(ByteCodeHandler) public: diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 16b5e02969..ff9f23b1eb 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -80,7 +80,7 @@ struct ControlFlow; struct ControlFlowCatch; struct ControlFlowFinally; -class Q_QMLCOMPILER_PRIVATE_EXPORT CodegenWarningInterface +class Q_QML_COMPILER_PRIVATE_EXPORT CodegenWarningInterface { public: virtual void reportVarUsedBeforeDeclaration(const QString &name, const QString &fileName, @@ -95,7 +95,7 @@ inline CodegenWarningInterface *defaultCodegenWarningInterface() return &iface; } -class Q_QMLCOMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor +class Q_QML_COMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor { protected: using BytecodeGenerator = QV4::Moth::BytecodeGenerator; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 65fe7bc85e..677d78a617 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -78,7 +78,7 @@ struct Module; struct Class; struct TemplateObject; -struct Q_QMLCOMPILER_PRIVATE_EXPORT StringTableGenerator { +struct Q_QML_COMPILER_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); int registerString(const QString &str); @@ -105,7 +105,7 @@ private: bool frozen = false; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { +struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator { static void generateUnitChecksum(CompiledData::Unit *unit); struct MemberInfo { 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 dabe6bfabe..0077e655bb 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/doc/images/qmlsc-compilation-scheme.png b/src/qml/doc/images/qmlsc-compilation-scheme.png Binary files differnew file mode 100644 index 0000000000..8a7785ea4b --- /dev/null +++ b/src/qml/doc/images/qmlsc-compilation-scheme.png 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 05ca519437..44849d8153 100644 --- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc +++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc @@ -75,6 +75,9 @@ qt_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 This command defines a QML module that can consist of C++ sources, \c{.qml} @@ -128,6 +131,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 @@ -283,6 +290,17 @@ 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 diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 5539804fe9..3f0f9a7077 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -338,10 +338,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 diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc index 86a9f1d1f7..a73eb92416 100644 --- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc +++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc @@ -274,9 +274,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/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index 0999abc5af..cd8c895e17 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -26,52 +26,39 @@ ****************************************************************************/ /*! - \externalpage http://www.ecma-international.org/publications/standards/Ecma-262.htm + \externalpage https://www.ecma-international.org/publications-and-standards/standards/ecma-262/ \title ECMA-262 */ /*! - \externalpage http://qmlbook.github.io/ - \title Qt5 Cadaques -*/ - -/*! - \externalpage http://www.w3schools.com/jsref/default.asp + \externalpage https://www.w3schools.com/jsref/default.asp \title W3Schools JavaScript Reference */ /*! \externalpage https://tc39.es/ecma262/#sec-date-objects - \title {ECMAScript Specification of Date} + \title ECMAScript Specification of Date */ /*! - \externalpage https://www.froglogic.com/squish/gui-testing - \title Squish -*/ -/*! - \externalpage http://doc.qt.io/GammaRay - \title GammaRay -*/ -/*! - \externalpage http://doc.qt.io/QtQmlLive - \title QmlLive + \externalpage https://www.qt.io/product/testing-tools#squish + \title Squish GUI Test Automation Tool */ /*! - \externalpage http://doc.qt.io/qtcreator/creator-debugging-qml.html - \title QML Debugger + \externalpage https://doc.qt.io/GammaRay + \title GammaRay Manual */ /*! - \externalpage http://doc.qt.io/qtcreator/creator-qml-performance-monitor.html - \title QML Profiler + \externalpage https://doc.qt.io/QtQmlLive + \title QmlLive Manual */ /*! - \externalpage http://doc.qt.io/qtcreator/index.html - \title Qt Creator Manual + \externalpage https://doc.qt.io/qtcreator/creator-debugging-qml.html + \title Qt Creator: QML Debugger */ /*! - \externalpage https://doc.qt.io/qtcreator/creator-project-creating.html#creating-resource-files - \title Creating Resource Files + \externalpage https://doc.qt.io/qtcreator/creator-qml-performance-monitor.html + \title Qt Creator: QML Profiler */ /*! \externalpage https://fontawesome.com/ diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index 99aa4cd058..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 diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc index 32c2e751a5..b5dd313348 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/modules/qmldir.qdoc b/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc index 95e8a179de..be1908f7cd 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 diff --git a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc index c37fb81401..fca702a278 100644 --- a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc +++ b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc @@ -38,6 +38,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/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 935e4b7673..33d179a985 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -686,8 +686,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 diff --git a/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc new file mode 100644 index 0000000000..cb25e8226e --- /dev/null +++ b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/*! +\page qtqml-qtquick-compiler-tech.html +\title Qt Quick Compiler +\brief Overview of Qt Quick Compiler components + +The Qt Quick Compiler consist of two components: +\list + \li \l {QML Type Compiler} + \li \l {QML Script Compiler} +\endlist + +The QML Type Compiler (\e qmltc) compiles QML object structures into C++ classes. +The QML Script Compiler compiles functions and expressions in QML files of an +application into C++ code. + +\e qmltc uses an all-or-nothing approach, and compilation simply fails if +some unsupported language feature is encountered. + +In the case of \e qmlsc JavaScript sets limitations on compiling with \e qmlsc. +For more information see \l {Limitations when compiling JavaScript}. +*/ diff --git a/src/qml/doc/src/qtqml-tool-qmlsc.qdoc b/src/qml/doc/src/qtqml-tool-qmlsc.qdoc new file mode 100644 index 0000000000..5546e858f8 --- /dev/null +++ b/src/qml/doc/src/qtqml-tool-qmlsc.qdoc @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/*! +\page qtqml-tool-qmlsc.html +\title QML Script Compiler +\brief A tool to compile functions and expressions in QML. + +The QML Script Compiler will compile functions and expressions in QML files of an +application into C++ code within limitations set by the nature of JavaScript. +It replaces \e qmlcachegen, and simply generates C++ code in addition to byte +code for functions that can be exhaustively analyzed. The following flow chart +explains the compilation of \e qmlsc. + +\image qmlsc-compilation-scheme.png + +\section1 Limitations when compiling JavaScript + +Many JavaScript constructs cannot be efficiently represented in C++. \e qmlsc +skips the C++ code generation for functions that contain such constructs and +only generates byte code to be interpreted. Although, most common QML expressions +are rather simple: value lookups on QObjects, arithmetics, simple if/else or loop +constructs. Those can easily be expressed in C++, and doing so makes your +application run faster. + +\e qmlsc is available for commercial customers and some of its features are +merged into \e qmlcachegen, which continues to be available with all versions of +Qt. +*/ diff --git a/src/qml/doc/src/qtqml-tool-qmltc.qdoc b/src/qml/doc/src/qtqml-tool-qmltc.qdoc index 1bdeae098c..e13d0dc0c6 100644 --- a/src/qml/doc/src/qtqml-tool-qmltc.qdoc +++ b/src/qml/doc/src/qtqml-tool-qmltc.qdoc @@ -123,7 +123,7 @@ Then the CMake code would usually look similar to the following: \codeline \snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp -\section3 Using the generated C++ +\section3 Using the Generated C++ Unlike in the case of QQmlComponent instantiation, the output of qmltc, being C++ code, is used directly by the application. Generally, constructing a new @@ -133,27 +133,36 @@ or, for example, combined with QQuickWindow to be drawn on screen. Given a \c{myApp.qml} file, the application code (in both cases) would typically look like this: -\table 100 % - -\header - \li Using QQmlComponent - \li Using qmltc-generated class -\row - \li \snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include - \li \snippet qmltc/tst_qmltc_examples.cpp qmltc-include -\row - \li \snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0 - \codeline - \snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1 - \codeline - \snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2 - \codeline - \snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec - - \li \snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code - \codeline - \snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec -\endtable +\if defined(onlinedocs) + \tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked} + \tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{} + \tabcontent {tab-qqmlcomponent} +\else + \section4 Using QQmlComponent +\endif +\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0 +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1 +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2 +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec +\if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-qmltc} +\else + \section4 Using qmltc-generated class +\endif +\snippet qmltc/tst_qmltc_examples.cpp qmltc-include +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code +\codeline +\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec +\if defined(onlinedocs) + \endtabcontent +\endif \section2 QML engine diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc index b8385c0ea7..a6f14fdc71 100644 --- a/src/qml/doc/src/qtqml.qdoc +++ b/src/qml/doc/src/qtqml.qdoc @@ -158,6 +158,8 @@ the QML code to interact with C++ code. Further information for writing QML applications: \list \li \l {The QML Reference} + \li \l {Qt Quick Compiler} + - overview of Qt Quick Compiler components \li \l {QML Applications} - essential information for application development with QML and Qt Quick diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 90965e10ac..80ef6bf39f 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -364,6 +364,9 @@ void ExecutionEngine::initializeStaticMembers() #elif defined(Q_OS_ANDROID) // In experiments, it started crashing at 1059. s_maxCallDepth = 1000; +#elif defined(Q_OS_WIN) + // We've seen crashes around 750. + s_maxCallDepth = 640; #else s_maxCallDepth = 1234; #endif @@ -431,12 +434,15 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) } } + // We allocate guard pages around our stacks. + const size_t guardPages = 2 * WTF::pageSize(); + memoryManager = new QV4::MemoryManager(this); // reserve space for the JS stack // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. *jsStack = WTF::PageAllocation::allocate( - s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, + s_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND @@ -445,9 +451,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); + *gcStack = WTF::PageAllocation::allocate( + s_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); exceptionValue = jsAlloca(1); *exceptionValue = Encode::undefined(); @@ -2228,7 +2234,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); @@ -2337,6 +2343,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; 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 337b86dbc0..399ce1dee2 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -140,8 +140,12 @@ struct ScopedStackFrame ScopedStackFrame(const Scope &scope, ExecutionContext *context) : engine(scope.engine) { - frame.init(engine->currentStackFrame ? engine->currentStackFrame->v4Function : nullptr, - nullptr, context, nullptr, nullptr, 0); + if (auto currentFrame = engine->currentStackFrame) { + frame.init(currentFrame->v4Function, nullptr, context, nullptr, nullptr, 0); + frame.instructionPointer = currentFrame->instructionPointer; + } else { + frame.init(nullptr, nullptr, context, nullptr, nullptr, 0); + } frame.push(engine); } diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 18f9e17f11..f31814d089 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 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/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index c27578ce7b..4a2c4ea0de 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) { 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/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 9e9e555a2f..502b8bafa1 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -795,7 +795,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); @@ -1958,14 +1958,16 @@ bool CallArgument::fromValue( // Convert via QVariant below. // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead? break; + } else if (QObject *obj = qmlTypeWrapper->object()) { + // attached object case + qobjectPtr = obj; + return true; } // If this is a plain type wrapper without an instance, - // then indeed it's an undefined parameter. - // (although we might interpret that as const QMetaObject *). - // TODO: But what if it's an attached object? + // then we got a namespace, and that's a type error type = QMetaType::UnknownType; - return true; + return false; } qobjectPtr = nullptr; diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp index e99dda591f..0bd495234d 100644 --- a/src/qml/jsruntime/qv4stackframe.cpp +++ b/src/qml/jsruntime/qv4stackframe.cpp @@ -55,7 +55,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) { @@ -63,9 +63,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/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp index c63e71ea46..0a55a1d876 100644 --- a/src/qml/jsruntime/qv4urlobject.cpp +++ b/src/qml/jsruntime/qv4urlobject.cpp @@ -167,7 +167,7 @@ void UrlObject::setUrl(const QUrl &url) d()->port.set(engine(), engine()->newString(url.port() == -1 ? QLatin1String("") : QString::number(url.port()))); - d()->protocol.set(engine(), engine()->newString(url.scheme())); + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); d()->search.set(engine(), engine()->newString(url.query())); d()->username.set(engine(), engine()->newString(url.userName())); @@ -222,15 +222,23 @@ bool UrlObject::setPort(QString port) return true; } -bool UrlObject::setProtocol(QString protocol) +bool UrlObject::setProtocol(QString protocolOrScheme) { QUrl url = toQUrl(); - url.setScheme(protocol); + // If there is one or several ':' in the protocolOrScheme, + // everything from the first colon is removed. + + qsizetype firstColonPos = protocolOrScheme.indexOf(QLatin1Char(':')); + + if (firstColonPos != -1) + protocolOrScheme.truncate(firstColonPos); + + url.setScheme(protocolOrScheme); if (!url.isValid()) return false; - d()->protocol.set(engine(), engine()->newString(url.scheme())); + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 63e02ce631..8ea0c94e7a 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; } @@ -525,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/parser/qqmljs.g b/src/qml/parser/qqmljs.g index e20e861f8b..d4629f2bdd 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -1663,6 +1663,9 @@ Type: UiQualifiedId; } break; ./ +Type: T_VAR; +/. case $rule_number: Q_FALLTHROUGH(); ./ + Type: T_VOID; /. case $rule_number: { diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 8781e4bf50..b9c4865336 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -223,6 +223,7 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) /*! * \enum QQmlModuleImportSpecialVersions + * \relates QQmlEngine * * Defines some special values that can be passed to the version arguments of * qmlRegisterModuleImport() and qmlUnregisterModuleImport(). @@ -241,25 +242,26 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) */ /*! - * Registers an implicit import for module \a uri of major version \a majorVersion. + * \relates QQmlEngine + * Registers an implicit import for module \a uri of major version \a moduleMajor. * * This has the same effect as an \c import statement in a qmldir file: Whenever * \a uri of version \a moduleMajor is imported, \a import of version * \a importMajor. \a importMinor is automatically imported, too. If - * \a importMajor is \l QmlModuleImportLatest the latest version + * \a importMajor is \l QQmlModuleImportLatest the latest version * available of that module is imported, and \a importMinor does not matter. If - * \a importMinor is \l QmlModuleImportLatest the latest minor version of a - * \a importMajor is chosen. If \a importMajor is \l QmlModuleImportAuto the + * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a + * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the * version of \a import is version of \a uri being imported, and \a importMinor - * does not matter. If \a moduleMajor is \a QmlModuleImportModuleAny the module + * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module * import is applied for any major version of \a uri. For example, you may * specify that whenever any version of MyModule is imported, the latest version * of MyOtherModule should be imported. Then, the following call would be * appropriate: * * \code - * qmlRegisterModuleImport("MyModule", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportLatest); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportLatest); * \endcode * * Or, you may specify that whenever major version 5 of "MyModule" is imported, @@ -273,8 +275,8 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) * imported whenever "MyModule" is imported, specify the following: * * \code - * qmlRegisterModuleImport("MyModule", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportAuto); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportAuto); * \endcode * * \sa qmlUnregisterModuleImport() @@ -289,6 +291,7 @@ void qmlRegisterModuleImport(const char *uri, int moduleMajor, /*! + * \relates QQmlEngine * Removes a module import previously registered with qmlRegisterModuleImport() * * Calling this function makes sure that \a import of version @@ -506,7 +509,7 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) const QTypeRevision added = revisionClassInfo( type.classInfoMetaObject, "QML.AddedInVersion", - QTypeRevision::fromMinorVersion(0)); + QTypeRevision::fromVersion(type.version.majorVersion(), 0)); const QTypeRevision removed = revisionClassInfo( type.classInfoMetaObject, "QML.RemovedInVersion"); const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject, diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index addee70b6b..f54eee3e72 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -862,8 +862,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; 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/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e3840c8011..9963e4d957 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -163,7 +163,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() diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 630d32736b..97c2f95450 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -80,6 +80,8 @@ #include <QtCore/qstring.h> #include <QtCore/qthread.h> +#include <atomic> + QT_BEGIN_NAMESPACE class QNetworkAccessManager; @@ -250,7 +252,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/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp index ecac16102d..3a73a5eae1 100644 --- a/src/qml/qml/qqmlextensionplugin.cpp +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -184,7 +184,8 @@ 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. diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 4db8981975..181efe49aa 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -65,6 +65,9 @@ static char file_string[] = "file"; #if defined(Q_OS_ANDROID) static char assets_string[] = "assets"; static char content_string[] = "content"; +static char authority_externalstorage[] = "com.android.externalstorage.documents"; +static char authority_downloads_documents[] = "com.android.providers.downloads.documents"; +static char authority_media_documents[] = "com.android.providers.media.documents"; #endif class QQmlFilePrivate; @@ -505,6 +508,17 @@ bool QQmlFile::isSynchronous(const QString &url) return false; } +#if defined(Q_OS_ANDROID) +static bool hasLocalContentAuthority(const QUrl &url) +{ + const QString authority = url.authority(); + return authority.isEmpty() + || authority == QLatin1String(authority_externalstorage) + || authority == QLatin1String(authority_downloads_documents) + || authority == QLatin1String(authority_media_documents); +} +#endif + /*! Returns true if \a url is a local file that can be opened with QFile. @@ -516,58 +530,116 @@ 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)) + return url.authority().isEmpty(); + if (scheme.length() == 7 + && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive)) + return hasLocalContentAuthority(url); #endif - } else { + return false; +} + +static bool hasScheme(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; + + return true; +} + +static qsizetype authorityOffset(const QString &url, qsizetype schemeLength) +{ + const qsizetype urlLength = url.length(); + + if (urlLength < schemeLength + 3) + return -1; + + const QLatin1Char slash('/'); + if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) { + // Exactly two slashes denote an authority. + if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash) + return schemeLength + 3; } + + return -1; } +#if defined(Q_OS_ANDROID) +static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength) +{ + const qsizetype offset = authorityOffset(url, schemeLength); + if (offset == -1) + return true; // no authority is a local authority. + + const QString authorityAndPath = url.sliced(offset); + return authorityAndPath.startsWith(QLatin1String(authority_externalstorage)) + || authorityAndPath.startsWith(QLatin1String(authority_downloads_documents)) + || authorityAndPath.startsWith(QLatin1String(authority_media_documents)); +} + +#endif + /*! 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 hasScheme(url, qrc_string, strlen(qrc_string)) + && authorityOffset(url, strlen(qrc_string)) == -1; #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 hasScheme(url, assets_string, strlen(assets_string)) + && authorityOffset(url, strlen(assets_string)) == -1; + case 'c': + case 'C': + return hasScheme(url, content_string, strlen(content_string)) + && hasLocalContentAuthority(url, strlen(content_string)); #endif + default: + break; + } return false; } @@ -585,15 +657,14 @@ 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()) + 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) { + if (hasLocalContentAuthority(url)) return url.toString(); return QString(); - } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) { - return url.toString(); } #endif - return url.toLocalFile(); } @@ -603,11 +674,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 +703,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 (hasScheme(url, content_string, strlen(content_string))) + return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString(); #endif return toLocalFile(url); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 610a39a992..9feb7a5dfc 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -236,7 +236,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()); @@ -253,40 +254,41 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } - // 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); + 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); - // More complex - need to search name - QByteArray name = method.name(); + // More complex - need to search name + QByteArray name = method.name(); + bool found = false; - bool found = false; + for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); + !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { - for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); - !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); - ++ii) { + QMetaMethod other = ignoreEnd->method(ii); - QMetaMethod other = ignoreEnd->method(ii); + found = name == other.name(); + } - found = name == other.name(); + QMetaMethodBuilder m = builder.addMethod(method); + if (found) // SKIP + m.setAccess(QMetaMethod::Private); } - QMetaMethodBuilder m = builder.addMethod(method); - if (found) // SKIP - m.setAccess(QMetaMethod::Private); - } - - // Clone Q_PROPERTY - for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { - QMetaProperty property = mo->property(ii); + // Clone Q_PROPERTY + for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { + QMetaProperty property = mo->property(ii); - int otherIndex = ignoreEnd->indexOfProperty(property.name()); - if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { - builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); - // Skip - } else { - builder.addProperty(property); + 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); + } } } @@ -851,6 +853,8 @@ QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes( QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); } +#else + Q_UNUSED(basePath) #endif const QByteArray bytes = uri.toUtf8(); @@ -1519,7 +1523,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); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 2b00b75dc6..7777562454 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -243,8 +243,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/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index 7243615e3e..a73396a892 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -167,7 +167,8 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP void QQmlPropertyBindingJS::expressionChanged() { - if (!asBinding()->propertyDataPtr) + auto binding = asBinding(); + if (!binding->propertyDataPtr) return; const auto currentTag = m_error.tag(); if (currentTag == InEvaluationLoop) { @@ -187,8 +188,9 @@ void QQmlPropertyBindingJS::expressionChanged() return; } m_error.setTag(InEvaluationLoop); - asBinding()->evaluateRecursive(); - asBinding()->notifyRecursive(); + PendingBindingObserverList bindingObservers; + binding->evaluateRecursive(bindingObservers); + binding->notifyNonRecursive(bindingObservers); m_error.setTag(NoTag); } diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 5b44ead713..278977fb9c 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1038,6 +1038,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const 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/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index 45edb59cfb..fe68337eb3 100644 --- a/src/qml/qml/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -115,8 +115,10 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() return true; } + if (!referencingObjectPropertyCache) + return false; + Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; @@ -149,15 +151,22 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePr if (propertyCaches->at(groupPropertyObjectIndex)) continue; - if (pendingBinding.referencingObjectPropertyCache) { - if (!pendingBinding.resolveInstantiatingProperty()) - continue; - auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); - propertyCaches->set(groupPropertyObjectIndex, cache); - } else { + if (pendingBinding.instantiatingPropertyName.isEmpty()) { + // Generalized group property. auto cache = propertyCaches->at(pendingBinding.referencingObjectIndex); propertyCaches->set(groupPropertyObjectIndex, cache); + continue; } + + if (!pendingBinding.referencingObjectPropertyCache) { + pendingBinding.referencingObjectPropertyCache + = propertyCaches->at(pendingBinding.referencingObjectIndex); + } + + if (!pendingBinding.resolveInstantiatingProperty()) + continue; + auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); + propertyCaches->set(groupPropertyObjectIndex, cache); } } diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 606628cbe8..09d8ace425 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -348,12 +348,8 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur switch (binding->type()) { case QV4::CompiledData::Binding::Type_Object: case QV4::CompiledData::Binding::Type_GroupProperty: - // We can resolve object and group properties if we have a property cache. - if (thisCache) - break; - continue; case QV4::CompiledData::Binding::Type_AttachedProperty: - // We can always resolve attached properties. + // We can always resolve object, group, and attached properties. break; default: // Everything else is of no interest here. @@ -949,10 +945,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(); diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index 33b216ee78..a91652e9a1 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/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index f827a24660..748283ad03 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -219,16 +219,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); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = mo; QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 }; 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/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index fe371e6b4f..691d03d445 100644 --- a/src/qml/qmldirparser/qqmldirparser_p.h +++ b/src/qml/qmldirparser/qqmldirparser_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE class QQmlEngine; -class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlDirParser +class Q_QML_COMPILER_PRIVATE_EXPORT QQmlDirParser { public: void clear(); diff --git a/src/qml/qmldirparser/qqmlimportresolver.cpp b/src/qml/qmldirparser/qqmlimportresolver.cpp index 25b366556d..fd24bd7268 100644 --- a/src/qml/qmldirparser/qqmlimportresolver.cpp +++ b/src/qml/qmldirparser/qqmlimportresolver.cpp @@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned }; -/*! +/* Forms complete paths to a module, from a list of base paths, a module URI and version specification. diff --git a/src/qml/qmldirparser/qqmlimportresolver_p.h b/src/qml/qmldirparser/qqmlimportresolver_p.h index 743e9f5526..61ceb91524 100644 --- a/src/qml/qmldirparser/qqmlimportresolver_p.h +++ b/src/qml/qmldirparser/qqmlimportresolver_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE -Q_QMLCOMPILER_PRIVATE_EXPORT QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, +Q_QML_COMPILER_PRIVATE_EXPORT QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths, QTypeRevision version); QT_END_NAMESPACE diff --git a/src/qml/qtqmlcompilerglobal.h b/src/qml/qtqmlcompilerglobal.h index 81bd63eb95..732bc51be0 100644 --- a/src/qml/qtqmlcompilerglobal.h +++ b/src/qml/qtqmlcompilerglobal.h @@ -45,12 +45,12 @@ QT_BEGIN_NAMESPACE #if defined(QT_STATIC) -# define Q_QMLCOMPILER_EXPORT +# define Q_QML_COMPILER_EXPORT #else # if defined(QT_BUILD_QML_LIB) -# define Q_QMLCOMPILER_EXPORT Q_DECL_EXPORT +# define Q_QML_COMPILER_EXPORT Q_DECL_EXPORT # else -# define Q_QMLCOMPILER_EXPORT Q_DECL_IMPORT +# define Q_QML_COMPILER_EXPORT Q_DECL_IMPORT # endif #endif diff --git a/src/qml/qtqmlcompilerglobal_p.h b/src/qml/qtqmlcompilerglobal_p.h index 9c8bce23d3..0f35231421 100644 --- a/src/qml/qtqmlcompilerglobal_p.h +++ b/src/qml/qtqmlcompilerglobal_p.h @@ -54,6 +54,6 @@ #include <QtCore/private/qglobal_p.h> #include <qtqmlcompilerglobal.h> -#define Q_QMLCOMPILER_PRIVATE_EXPORT Q_QMLCOMPILER_EXPORT +#define Q_QML_COMPILER_PRIVATE_EXPORT Q_QML_COMPILER_EXPORT #endif // QTQMLCOMPILERGLOBAL_P_H |