aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorTarja Sundqvist <tarja.sundqvist@qt.io>2025-10-12 23:00:20 +0300
committerTarja Sundqvist <tarja.sundqvist@qt.io>2025-10-12 23:00:20 +0300
commit4826f86e274f1b29bd769e6790824f9e62a40f62 (patch)
treec2cc4bb09ca91951a2641e33c3b0a65deb0af877 /src/qml
parent0f0972d542d9869c2dcfaf9c963d42ff32766460 (diff)
parent844f9b9b376838bcb44324984876f8bf99d85d38 (diff)
Merge tag 'v6.5.7-lts-lgpl' into 6.56.5
Qt 6.5.7-lts-lgpl release
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/Qt6QmlMacros.cmake11
-rw-r--r--src/qml/common/qv4staticvalue_p.h4
-rw-r--r--src/qml/configure.cmake2
-rw-r--r--src/qml/doc/snippets/qml/function-call-binding.qml18
-rw-r--r--src/qml/doc/src/external-resources.qdoc4
-rw-r--r--src/qml/doc/src/javascript/qmlglobalobject.qdoc4
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc4
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc7
-rw-r--r--src/qml/jsruntime/qv4engine.cpp16
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4object.cpp6
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp10
-rw-r--r--src/qml/memory/qv4heap_p.h2
-rw-r--r--src/qml/qml/qqml.cpp19
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h2
-rw-r--r--src/qml/qml/qqmlbinding.cpp9
-rw-r--r--src/qml/qml/qqmlbinding_p.h2
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp24
-rw-r--r--src/qml/qml/qqmlcomponent.cpp10
-rw-r--r--src/qml/qml/qqmlengine.cpp19
-rw-r--r--src/qml/qml/qqmlimport.cpp2
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp82
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h4
-rw-r--r--src/qml/qml/qqmlproperty.cpp53
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding.cpp3
25 files changed, 257 insertions, 62 deletions
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 3c1d7870e7..49829d8e61 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -828,7 +828,7 @@ function(_qt_internal_target_enable_qmllint target)
)
set(cmd
- ${tool_wrapper}
+ "${tool_wrapper}"
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
@${qmllint_rsp_path}
)
@@ -860,7 +860,7 @@ function(_qt_internal_target_enable_qmllint target)
)
set(cmd
- ${tool_wrapper}
+ "${tool_wrapper}"
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
@${qmllint_rsp_path}
)
@@ -898,7 +898,7 @@ function(_qt_internal_target_enable_qmllint target)
)
set(cmd
- ${tool_wrapper}
+ "${tool_wrapper}"
$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
@${qmllint_rsp_path}
)
@@ -1069,7 +1069,7 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
set(cmd
- ${tool_wrapper}
+ "${tool_wrapper}"
${qmlcachegen}
--resource-name "${qmlcache_resource_name}"
-o "${qmlcache_loader_cpp}"
@@ -2486,6 +2486,9 @@ function(qt6_generate_foreign_qml_types source_target destination_qml_target)
VERBATIM
)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.27")
+ set_source_files_properties(${additional_sources} PROPERTIES SKIP_LINTING ON)
+ endif()
target_sources(${destination_qml_target} PRIVATE ${additional_sources})
endfunction()
diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h
index e9c3554104..5c50df30c6 100644
--- a/src/qml/common/qv4staticvalue_p.h
+++ b/src/qml/common/qv4staticvalue_p.h
@@ -366,10 +366,10 @@ struct StaticValue
QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) {
if (qt_is_nan(d)) {
// We cannot store just any NaN. It has to be a NaN with only the quiet bit
- // set in the upper bits of the mantissa and the sign bit off.
+ // set in the upper bits of the mantissa and the sign bit either on or off.
// qt_qnan() happens to produce such a thing via std::numeric_limits,
// but this is actually not guaranteed. Therefore, we make our own.
- _val = (quint64(QuickType::NaN) << Tag_Shift);
+ _val = (quint64(std::signbit(d) ? QuickType::MinusNaN : QuickType::NaN) << Tag_Shift);
Q_ASSERT(isNaN());
} else {
memcpy(&_val, &d, 8);
diff --git a/src/qml/configure.cmake b/src/qml/configure.cmake
index 654fc2ec9d..28f9d955cb 100644
--- a/src/qml/configure.cmake
+++ b/src/qml/configure.cmake
@@ -14,7 +14,7 @@ qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST MODULE_NAME qml QMAKE_LIB l
qt_find_package(Python REQUIRED)
if(Python_Interpreter_FOUND)
# Need to make it globally available to the project
- set(QT_INTERNAL_DECLARATIVE_PYTHON "${Python_EXECUTABLE}" CACHE STRING "")
+ set(QT_INTERNAL_DECLARATIVE_PYTHON "${Python_EXECUTABLE}" CACHE STRING "" FORCE)
endif()
#### Tests
diff --git a/src/qml/doc/snippets/qml/function-call-binding.qml b/src/qml/doc/snippets/qml/function-call-binding.qml
new file mode 100644
index 0000000000..1dc7d0224c
--- /dev/null
+++ b/src/qml/doc/snippets/qml/function-call-binding.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+//! [rectangle]
+Rectangle {
+ x: rectPosition()
+ y: rectPosition()
+ width: 200
+ height: 200
+ color: "lightblue"
+
+ function rectPosition() {
+ return enabled ? 0 : 100
+ }
+}
+//! [rectangle]
diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc
index c120257247..02c39ee3fa 100644
--- a/src/qml/doc/src/external-resources.qdoc
+++ b/src/qml/doc/src/external-resources.qdoc
@@ -29,6 +29,10 @@
\title QmlLive Manual
*/
/*!
+ \externalpage https://felgo.com/qml-hot-reload
+ \title Felgo QML Hot Reload Tool
+*/
+/*!
\externalpage https://doc.qt.io/qtcreator/creator-debugging-qml.html
\title Qt Creator: QML Debugger
*/
diff --git a/src/qml/doc/src/javascript/qmlglobalobject.qdoc b/src/qml/doc/src/javascript/qmlglobalobject.qdoc
index 15b9996ff3..1bd03fad54 100644
--- a/src/qml/doc/src/javascript/qmlglobalobject.qdoc
+++ b/src/qml/doc/src/javascript/qmlglobalobject.qdoc
@@ -13,8 +13,8 @@ additional imports:
\list
\li The \l{QmlGlobalQtObject}{Qt object}: A QML object that offers helper methods
and properties specific to the QML environment.
-\li \l {Qt::}{qsTr()}, \l {Qt::}{qsTranslate()}, \l {Qt::}{qsTrId()}, \l {Qt::}{qsTrNoOp()},
- \l {Qt::}{qsTranslateNoOp()}, \l {Qt::}{qsTrIdNoOp()} functions:
+\li \l {Qt::}{qsTr()}, \l {Qt::}{qsTranslate()}, \l {Qt::}{qsTrId()}, \l {Qt::}{QT_TR_NOOP()()},
+ \l {Qt::}{QT_TRANSLATE_NOOP()}, \l {Qt::}{QT_TRID_NOOP()} functions:
QML functions that let you translate \l{Mark Strings for Translation}
{strings} and \l{Mark Translatable Data Text Strings}{string literals} in the
QML environment.
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index e2dfb6ca77..f6619d8d00 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -1467,6 +1467,10 @@
Returns -1 if no matching type was found or one of the given parameters
was invalid.
+ \note: qmlTypeId tries to make modules available, even if they were not accessed by any
+ engine yet. This can introduce overhead the first time a module is accessed. Trying to
+ find types from a module which does not exist always introduces this overhead.
+
\sa QML_ELEMENT, QML_NAMED_ELEMENT, QML_SINGLETON, qmlRegisterType(), qmlRegisterSingletonType()
*/
diff --git a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
index 65e3b95f8e..b45ad83fb8 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
@@ -98,6 +98,13 @@ In the previous example,
\li \c bottomRect.color depends on \c myTextInput.text.length
\endlist
+In addition, any properties referenced within a JavaScript function that is
+itself used as a binding will be re-evaluated. For example, in the snippet
+below, whenever the \c enabled property of the \c Rectangle changes, the
+bindings for the \c x and \c y properties will be re-evaluated:
+
+\snippet qml/function-call-binding.qml rectangle
+
Syntactically, bindings are allowed to be of arbitrary complexity. However, if
a binding is overly complex - such as involving multiple lines, or imperative
loops - it could indicate that the binding is being used for more than describing
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 342f642d89..99fad7e3c3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -2446,18 +2446,22 @@ bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
return false;
const QMetaType elementMetaType = iterable.valueMetaType();
- QVariant element(elementMetaType);
for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
- if (!ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()))
- element = QVariant(elementMetaType);
+ QVariant element(elementMetaType);
+ ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data());
iterable.addValue(element, QSequentialIterable::AtEnd);
}
return true;
}
-// Converts a JS value to a meta-type.
-// data must point to a place that can store a value of the given type.
-// Returns true if conversion succeeded, false otherwise.
+/*!
+ * \internal
+ *
+ * Converts a JS value to a meta-type.
+ * \a data must point to a default-constructed instance of \a metaType.
+ * Returns \c true if conversion succeeded, \c false otherwise. In the latter case,
+ * \a data is not modified.
+ */
bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data)
{
// check if it's one of the types we know
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 9f77d83ac3..d5519b49e5 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -52,6 +52,7 @@ Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Funct
proto->setPrototypeOf(scope.engine->generatorPrototype());
g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ g->d()->canBeTailCalled = false;
return g->d();
}
@@ -213,6 +214,7 @@ Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context,
proto->setPrototypeOf(scope.engine->generatorPrototype());
g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ g->d()->canBeTailCalled = false;
return g->d();
}
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 712666c6aa..0851b2f5ab 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -744,6 +744,12 @@ ReturnedValue Object::virtualResolveLookupGetter(const Object *object, Execution
Heap::Object *obj = object->d();
PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (object->as<QV4::ProxyObject>()) {
+ // proxies invalidate assumptions that we normally maek in lookups
+ // so we always need to use the fallback path
+ lookup->getter = Lookup::getterFallback;
+ return lookup->getter(lookup, engine, *object);
+ }
if (name.isArrayIndex()) {
lookup->indexedLookup.index = name.asArrayIndex();
lookup->getter = Lookup::getterIndexed;
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index 43dc0fae4f..e87bf957d2 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -762,8 +762,6 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b,
fin = static_cast<uint>(std::min(relativeEnd, dlen));
}
- double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN();
- Value value = Value::fromDouble(val);
if (scope.hasException() || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
@@ -771,6 +769,14 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b,
uint bytesPerElement = v->bytesPerElement();
uint byteOffset = v->byteOffset();
+ Value value;
+ if (!argc)
+ value.setDouble(std::numeric_limits<double>::quiet_NaN());
+ else if (argv[0].isNumber())
+ value = argv[0];
+ else
+ value.setDouble(argv[0].toNumber());
+
while (k < fin) {
v->d()->type->write(data + byteOffset + k * bytesPerElement, value);
k++;
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index b4bd3c4cd6..2d0ad3d601 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -221,7 +221,7 @@ struct QV4QPointer {
private:
QtSharedPointer::ExternalRefCountData *d;
- QObject *qObject;
+ T *qObject;
};
Q_STATIC_ASSERT(std::is_trivial_v<QV4QPointer<QObject>>);
#endif
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index e914da329e..94a91629f3 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -335,7 +335,24 @@ void qmlUnregisterModuleImport(const char *uri, int moduleMajor,
//From qqml.h
int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
- return QQmlMetaType::typeId(uri, QTypeRevision::fromVersion(versionMajor, versionMinor), qmlName);
+ auto revision = QTypeRevision::fromVersion(versionMajor, versionMinor);
+ int id = QQmlMetaType::typeId(uri, revision, qmlName);
+ if (id != -1)
+ return id;
+ /* If the module hasn't been imported yet, we might not have the id of a
+ singleton at this point. To obtain it, we need an engine in order to
+ to do the resolution steps.
+ This is expensive, but we assume that users don't constantly query invalid
+ Types; internal code should use QQmlMetaType API.
+ */
+ QQmlEngine engine;
+ auto *enginePriv = QQmlEnginePrivate::get(&engine);
+ auto loadHelper = QQml::makeRefPointer<LoadHelper>(&enginePriv->typeLoader, uri);
+ auto type = loadHelper->resolveType(qmlName).type;
+ if (type.availableInVersion(revision))
+ return type.index();
+ else
+ return -1;
}
static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h
index d4c00ea886..27faad645c 100644
--- a/src/qml/qml/qqmlabstractbinding_p.h
+++ b/src/qml/qml/qqmlabstractbinding_p.h
@@ -63,7 +63,7 @@ public:
void addToObject();
void removeFromObject();
- static void printBindingLoopError(const QQmlProperty &prop);
+ virtual void printBindingLoopError(const QQmlProperty &prop);
inline QQmlAbstractBinding *nextBinding() const;
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 8bf1a9a7d0..551f55b4be 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -5,6 +5,7 @@
#include "qqmlcontext.h"
#include "qqmldata_p.h"
+#include "qqmlinfo.h"
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
@@ -145,7 +146,7 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
getPropertyData(&d, &vtd);
Q_ASSERT(d);
QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, nullptr);
- QQmlAbstractBinding::printBindingLoopError(p);
+ printBindingLoopError(p);
return;
}
setUpdatingFlag(true);
@@ -167,6 +168,12 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
setUpdatingFlag(false);
}
+void QQmlBinding::printBindingLoopError(const QQmlProperty &prop)
+{
+ qmlWarning(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\":\n%2"))
+ .arg(prop.name(), expressionIdentifier());
+}
+
QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
{
QV4::ExecutionEngine *v4 = engine()->handle();
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 00aa0d6f58..b2f10b826a 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -73,6 +73,8 @@ public:
QString expression() const override;
void update(QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding);
+ void printBindingLoopError(const QQmlProperty &prop) override;
+
typedef int Identifier;
enum {
Invalid = -1
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 99e6f889b7..fdc28d727d 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -31,6 +31,7 @@
#include <private/qv4jsonobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4sequenceobject_p.h>
#include <private/qv4stackframe_p.h>
#include <QtCore/qstring.h>
@@ -230,7 +231,9 @@ The following functions are also on the Qt object.
The \c styleHints object provides platform-specific style hints and settings.
See the \l QStyleHints documentation for further details.
- You should access StyleHints via \l Application::styleHints instead.
+ You should access StyleHints via \l Application::styleHints instead, as
+ this provides better type information for tooling such as the
+ \l {Qt Quick Compiler}.
\note The \c styleHints object is only available when using the Qt Quick module.
*/
@@ -1782,14 +1785,20 @@ static QString serializeArray(Object *array, ExecutionEngine *v4, QSet<QV4::Heap
Scope scope(v4);
ScopedValue val(scope);
QString result;
-
alreadySeen.insert(array->d());
+
+ ScopedObject detached(scope);
+ if (Sequence *reference = array->as<Sequence>())
+ detached = ReferenceObject::detached(reference->d());
+ else
+ detached = array;
+
result += QLatin1Char('[');
- const uint length = array->getLength();
+ const uint length = detached->getLength();
for (uint i = 0; i < length; ++i) {
if (i != 0)
result += QLatin1Char(',');
- val = array->get(i);
+ val = detached->get(i);
if (val->isManaged() && val->managed()->isArrayLike())
if (!alreadySeen.contains(val->objectValue()->d()))
result += serializeArray(val->objectValue(), v4, alreadySeen);
@@ -1799,6 +1808,7 @@ static QString serializeArray(Object *array, ExecutionEngine *v4, QSet<QV4::Heap
result += val->toQStringNoThrow();
}
result += QLatin1Char(']');
+
alreadySeen.remove(array->d());
return result;
};
@@ -2139,7 +2149,7 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons
}
/*!
- \qmlmethod string Qt::qsTranslateNoOp(string context, string sourceText, string disambiguation)
+ \qmlmethod string Qt::QT_TRANSLATE_NOOP(string context, string sourceText, string disambiguation)
Marks \a sourceText for dynamic translation in the given \a context; i.e, the stored \a sourceText
will not be altered.
@@ -2250,7 +2260,7 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
}
/*!
- \qmlmethod string Qt::qsTrNoOp(string sourceText, string disambiguation)
+ \qmlmethod string Qt::QT_TR_NOOP(string sourceText, string disambiguation)
Marks \a sourceText for dynamic translation; i.e, the stored \a sourceText
will not be altered.
@@ -2331,7 +2341,7 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val
}
/*!
- \qmlmethod string Qt::qsTrIdNoOp(string id)
+ \qmlmethod string Qt::QT_TRID_NOOP(string id)
Marks \a id for dynamic translation.
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index c3f692eeea..5a6fd4c8d3 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -1455,22 +1455,24 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlC
}
/*!
- Set top-level \a properties of the \a component.
+ Set top-level \a properties of the \a object that was created from a
+ QQmlComponent.
This method provides advanced control over component instance creation.
In general, programmers should use
- \l QQmlComponent::createWithInitialProperties to create a component.
+ \l QQmlComponent::createWithInitialProperties to create an object instance
+ from a component.
Use this method after beginCreate and before completeCreate has been called.
If a provided property does not exist, a warning is issued.
\since 5.14
*/
-void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties)
+void QQmlComponent::setInitialProperties(QObject *object, const QVariantMap &properties)
{
Q_D(QQmlComponent);
for (auto it = properties.constBegin(); it != properties.constEnd(); ++it)
- d->setInitialProperty(component, it.key(), it.value());
+ d->setInitialProperty(object, it.key(), it.value());
}
/*
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 015a00d53b..84347dca11 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -21,6 +21,7 @@
#include <QtCore/qstandardpaths.h>
#include <QtCore/qmetaobject.h>
#include <QDebug>
+#include <private/qqmlcomponent_p.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdir.h>
@@ -1811,6 +1812,24 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
return QJSValue(QJSValue::UndefinedValue);
}
QObject *o = component.beginCreate(q->rootContext());
+ auto *compPriv = QQmlComponentPrivate::get(&component);
+ if (compPriv->state.hasUnsetRequiredProperties()) {
+ /* We would only get the errors from the component after (complete)Create.
+ We can't call create, as we need to convertAndInsert before completeCreate (otherwise
+ tst_qqmllanguage::compositeSingletonCircular fails).
+ On the other hand, we don't want to call cnovertAndInsert if we have an error
+ So create the unset required component errors manually.
+ */
+ delete o;
+ const auto requiredProperties = compPriv->state.requiredProperties();
+ QList<QQmlError> errors (requiredProperties->size());
+ for (const auto &reqProp: *requiredProperties)
+ errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(reqProp));
+ warning(errors);
+ v4engine()->throwError(QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded.").arg(QString::fromUtf8(type.typeName())));
+ return QJSValue(QJSValue::UndefinedValue);
+ }
+
value = q->newQObject(o);
singletonInstances.convertAndInsert(v4engine(), type, &value);
component.completeCreate();
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 32377f0502..f8f8b72191 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1423,7 +1423,7 @@ QTypeRevision QQmlImports::updateQmldirContent(
Q_ASSERT(database);
Q_ASSERT(errors);
- qDebug(lcQmlImport)
+ qCDebug(lcQmlImport)
<< "updateQmldirContent:" << qPrintable(baseUrl().toString())
<< uri << "to" << qmldirUrl << "as" << prefix;
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 42f79dcecd..e3c0ba19ae 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -17,6 +17,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcIncompatibleElement, "qt.qml.list.incompatible")
+
using namespace QV4;
using namespace Qt::StringLiterals;
@@ -127,7 +129,22 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
QV4::Scope scope(v4);
QV4::ScopedObject so(scope, value.toObject(scope.engine));
if (auto *wrapper = so->as<QV4::QObjectWrapper>()) {
- prop->replace(prop, index, wrapper->object());
+ QObject *object = wrapper->object();
+ if (!object) {
+ prop->replace(prop, index, object);
+ return true;
+ }
+
+ const QMetaType elementType = w->d()->elementType();
+ const QMetaObject *elementMeta = elementType.metaObject();
+ if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
+ qCWarning(lcIncompatibleElement)
+ << "Cannot insert" << object << "into a QML list of" << elementType.name();
+ prop->replace(prop, index, nullptr);
+ return true;
+ }
+
+ prop->replace(prop, index, object);
return true;
}
@@ -242,11 +259,28 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
if (!qIsAtMostUintLimit(length, std::numeric_limits<uint>::max() - argc))
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+ const QMetaType elementType = w->d()->elementType();
+ const QMetaObject *elementMeta = elementType.metaObject();
for (int i = 0; i < argc; ++i) {
- if (argv[i].isNull())
+ if (argv[i].isNull()) {
property->append(property, nullptr);
- else
- property->append(property, argv[i].as<QV4::QObjectWrapper>()->object());
+ continue;
+ }
+
+ QObject *object = argv[i].as<QV4::QObjectWrapper>()->object();
+ if (!object) {
+ property->append(property, nullptr);
+ continue;
+ }
+
+ if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
+ qCWarning(lcIncompatibleElement)
+ << "Cannot append" << object << "to a QML list of" << elementType.name();
+ property->append(property, nullptr);
+ continue;
+ }
+
+ property->append(property, object);
}
const auto actualLength = property->count(property);
@@ -370,12 +404,29 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons
}
}
+ const QMetaType elementType = w->d()->elementType();
+ const QMetaObject *elementMeta = elementType.metaObject();
for (qsizetype i = 0; i < itemCount; ++i) {
const auto arg = argv[i + 2];
- if (arg.isNull())
+ if (arg.isNull()) {
property->replace(property, start + i, nullptr);
- else
- property->replace(property, start + i, arg.as<QObjectWrapper>()->object());
+ continue;
+ }
+
+ QObject *object = arg.as<QObjectWrapper>()->object();
+ if (!object) {
+ property->replace(property, start + i, nullptr);
+ continue;
+ }
+
+ if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
+ qCWarning(lcIncompatibleElement)
+ << "Cannot splice" << object << "into a QML list of" << elementType.name();
+ property->replace(property, start + i, nullptr);
+ continue;
+ }
+
+ property->replace(property, start + i, object);
}
return newArray->asReturnedValue();
@@ -420,9 +471,24 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
for (qsizetype k = len; k > 0; --k)
property->replace(property, k + argc - 1, property->at(property, k - 1));
+ const QMetaType elementType = w->d()->elementType();
+ const QMetaObject *elementMeta = elementType.metaObject();
for (int i = 0; i < argc; ++i) {
const auto *wrapper = argv[i].as<QObjectWrapper>();
- property->replace(property, i, wrapper ? wrapper->object() : nullptr);
+ QObject *object = wrapper ? wrapper->object() : nullptr;
+ if (!object) {
+ property->replace(property, i, object);
+ continue;
+ }
+
+ if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
+ qCWarning(lcIncompatibleElement)
+ << "Cannot unshift" << object << "into a QML list of" << elementType.name();
+ property->replace(property, i, nullptr);
+ continue;
+ }
+
+ property->replace(property, i, object);
}
return Encode(uint(len + argc));
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 21240d6b98..78872a61bb 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -16,6 +16,7 @@
//
#include <QtCore/qglobal.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qpointer.h>
#include <QtQml/qqmllist.h>
@@ -25,6 +26,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcIncompatibleElement)
+
namespace QV4 {
namespace Heap {
@@ -34,6 +37,7 @@ struct QmlListWrapper : Object {
void destroy();
QV4QPointer<QObject> object;
+ QMetaType elementType() const { return QQmlMetaType::listValueType(QMetaType(propertyType)); }
QQmlListProperty<QObject> &property() {
return *reinterpret_cast<QQmlListProperty<QObject>*>(propertyData);
}
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 7ebc602fff..1430c20099 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -19,6 +19,7 @@
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmllistwrapper_p.h>
#include <QtQml/private/qqmllist_p.h>
#include <QStringList>
@@ -867,17 +868,21 @@ static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPrope
oldBinding = data->bindings;
while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex ||
- oldBinding->targetPropertyIndex().hasValueTypeIndex()))
+ oldBinding->targetPropertyIndex().hasValueTypeIndex())) {
oldBinding = oldBinding->nextBinding();
+ }
- if (!oldBinding)
- return;
-
- if (valueTypeIndex != -1 && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy)
+ if (valueTypeIndex != -1
+ && oldBinding
+ && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy) {
oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index);
+ }
- if (!oldBinding)
+ if (!oldBinding) {
+ // Clear the binding bit so that the binding doesn't appear later for any reason
+ data->clearBindingBit(coreIndex);
return;
+ }
if (!(flags & QQmlPropertyPrivate::DontEnable))
oldBinding->setEnabled(false, {});
@@ -1402,18 +1407,6 @@ static ConvertAndAssignResult tryConvertAndAssign(
return {false, false};
}
- if (variantMetaType == QMetaType::fromType<QJSValue>()) {
- // Handle Qt.binding bindings here to avoid mistaken conversion below
- const QJSValue &jsValue = *static_cast<const QJSValue *>(value.constData());
- const QV4::FunctionObject *f
- = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
- if (f && f->isBinding()) {
- QV4::QObjectWrapper::setProperty(
- f->engine(), object, &property, f->asReturnedValue());
- return {true, true};
- }
- }
-
// common cases:
switch (propertyMetaType.id()) {
case QMetaType::Bool:
@@ -1498,6 +1491,21 @@ bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op)
return true;
}
+static bool tryAssignBinding(
+ QObject *object, const QQmlPropertyData &property, const QVariant &value,
+ QMetaType variantMetaType) {
+ if (variantMetaType != QMetaType::fromType<QJSValue>())
+ return false;
+
+ const QJSValue &jsValue = *static_cast<const QJSValue *>(value.constData());
+ const QV4::FunctionObject *f = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
+ if (!f || !f->isBinding())
+ return false;
+
+ QV4::QObjectWrapper::setProperty(f->engine(), object, &property, f->asReturnedValue());
+ return true;
+}
+
bool QQmlPropertyPrivate::write(
QObject *object, const QQmlPropertyData &property, const QVariant &value,
const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
@@ -1523,6 +1531,10 @@ bool QQmlPropertyPrivate::write(
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
const bool isUrl = propertyMetaType == QMetaType::fromType<QUrl>(); // handled separately
+ // Handle Qt.binding bindings here to avoid mistaken conversion below
+ if (tryAssignBinding(object, property, value, variantMetaType))
+ return true;
+
// The cases below are in approximate order of likelyhood:
if (propertyMetaType == variantMetaType && !isUrl
&& propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) {
@@ -1598,8 +1610,11 @@ bool QQmlPropertyPrivate::write(
prop.clear(&prop);
const auto doAppend = [&](QObject *o) {
- if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
+ if (Q_UNLIKELY(o && !QQmlMetaObject::canConvert(o, valueMetaObject))) {
+ qCWarning(lcIncompatibleElement)
+ << "Cannot append" << o << "to a QML list of" << listValueType.name();
o = nullptr;
+ }
prop.append(&prop, o);
};
diff --git a/src/qml/qml/qqmlpropertytopropertybinding.cpp b/src/qml/qml/qqmlpropertytopropertybinding.cpp
index ec77e8c97a..f2edf3b87f 100644
--- a/src/qml/qml/qqmlpropertytopropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertytopropertybinding.cpp
@@ -91,8 +91,7 @@ void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags)
// Check for a binding update loop
if (Q_UNLIKELY(updatingFlag())) {
- QQmlAbstractBinding::printBindingLoopError(
- QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
+ printBindingLoopError(QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
return;
}