aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlpropertybinding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlpropertybinding.cpp')
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp453
1 files changed, 237 insertions, 216 deletions
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index e600748be7..c8a7e6256a 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -1,92 +1,44 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlpropertybinding_p.h"
+
+#include <private/qqmlbinding_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmlscriptstring_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4jscall_p.h>
-#include <qqmlinfo.h>
+#include <private/qv4qmlcontext_p.h>
+
+#include <QtQml/qqmlinfo.h>
+
+#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
-namespace {
-constexpr std::size_t jsExpressionOffsetLength() {
- struct composite { QQmlPropertyBinding b; QQmlPropertyBindingJS js; };
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
- return sizeof (QQmlPropertyBinding) - offsetof(composite, js);
- QT_WARNING_POP
-}
-}
+using namespace Qt::Literals::StringLiterals;
-const QQmlPropertyBindingJS *QQmlPropertyBinding::jsExpression() const
-{
- return std::launder(reinterpret_cast<QQmlPropertyBindingJS const *>(reinterpret_cast<std::byte const*>(this) + sizeof(QQmlPropertyBinding) + jsExpressionOffsetLength()));
-}
+Q_LOGGING_CATEGORY(lcQQPropertyBinding, "qt.qml.propertybinding");
QUntypedPropertyBinding QQmlPropertyBinding::create(const QQmlPropertyData *pd, QV4::Function *function,
QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
{
- if (auto aotFunction = function->aotFunction; aotFunction && aotFunction->returnType == pd->propType()) {
- return QUntypedPropertyBinding(aotFunction->returnType,
- [
- aotFunction,
- unit = QQmlRefPointer<QV4::ExecutableCompilationUnit>(function->executableCompilationUnit()),
- scopeObject = QPointer<QObject>(obj),
- context = ctxt,
- engine = scope->engine()
- ](const QMetaType &, void *dataPtr) -> bool {
- QQmlPrivate::AOTCompiledContext aotContext;
- aotContext.qmlContext = context->asQQmlContext();
- aotContext.qmlScopeObject = scopeObject.data();
- aotContext.engine = engine->jsEngine();
- aotContext.compilationUnit = unit.data();
- aotFunction->functionPtr(&aotContext, dataPtr, nullptr);
- // ### Fixme: The aotFunction should do the check whether old and new value are the same and
- // return false in that case
- return true;
- },
- QPropertyBindingSourceLocation());
- }
+ Q_ASSERT(pd);
+ return create(pd->propType(), function, obj, ctxt, scope, target, targetIndex);
+}
- auto buffer = new std::byte[sizeof(QQmlPropertyBinding)+sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
- auto binding = new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, false);
- auto js = new(buffer + sizeof(QQmlPropertyBinding) + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
+QUntypedPropertyBinding QQmlPropertyBinding::create(QMetaType propertyType, QV4::Function *function,
+ QObject *obj,
+ const QQmlRefPointer<QQmlContextData> &ctxt,
+ QV4::ExecutionContext *scope, QObject *target,
+ QQmlPropertyIndex targetIndex)
+{
+ auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
+ + sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
+ auto binding = new (buffer) QQmlPropertyBinding(propertyType, target, targetIndex,
+ TargetData::WithoutBoundFunction);
+ auto js = new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
Q_ASSERT(binding->jsExpression() == js);
Q_ASSERT(js->asBinding() == binding);
Q_UNUSED(js);
@@ -99,9 +51,10 @@ QUntypedPropertyBinding QQmlPropertyBinding::create(const QQmlPropertyData *pd,
QUntypedPropertyBinding QQmlPropertyBinding::createFromCodeString(const QQmlPropertyData *pd, const QString& str, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber, QObject *target, QQmlPropertyIndex targetIndex)
{
- auto buffer = new std::byte[sizeof(QQmlPropertyBinding)+sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
- auto binding = new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, true);
- auto js = new(buffer + sizeof(QQmlPropertyBinding) + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
+ auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
+ + sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
+ auto binding = new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
+ auto js = new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
Q_ASSERT(binding->jsExpression() == js);
Q_ASSERT(js->asBinding() == binding);
Q_UNUSED(js);
@@ -111,11 +64,50 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromCodeString(const QQmlProp
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
}
+QUntypedPropertyBinding QQmlPropertyBinding::createFromScriptString(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt, QObject *target, QQmlPropertyIndex targetIndex)
+{
+ const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
+ // without a valid context, we cannot create anything
+ if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) {
+ return {};
+ }
+
+ auto scopeObject = obj ? obj : scriptPrivate->scope;
+
+ QV4::Function *runtimeFunction = nullptr;
+ QString url;
+ QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context);
+ QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
+ if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) {
+ url = ctxtdata->urlString();
+ if (scriptPrivate->bindingId != QQmlBinding::Invalid)
+ runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId);
+ }
+ // Do we actually have a function in the script string? If not, this becomes createCodeFromString
+ if (!runtimeFunction)
+ return createFromCodeString(property, scriptPrivate->script, obj, ctxtdata, url, scriptPrivate->lineNumber, target, targetIndex);
+
+ auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
+ + sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
+ auto binding = new(buffer) QQmlPropertyBinding(QMetaType(property->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
+ auto js = new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
+ Q_ASSERT(binding->jsExpression() == js);
+ Q_ASSERT(js->asBinding() == binding);
+ js->setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
+
+ QV4::ExecutionEngine *v4 = engine->v4engine();
+ QV4::Scope scope(v4);
+ QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, scopeObject));
+ js->setupFunction(qmlContext, runtimeFunction);
+ return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
+}
+
QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
{
- auto buffer = new std::byte[sizeof(QQmlPropertyBinding)+sizeof(QQmlPropertyBindingJSForBoundFunction)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
- auto binding = new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, true);
- auto js = new(buffer + sizeof(QQmlPropertyBinding) + jsExpressionOffsetLength()) QQmlPropertyBindingJSForBoundFunction();
+ auto buffer = new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
+ + sizeof(QQmlPropertyBindingJSForBoundFunction)+jsExpressionOffsetLength()]; // QQmlPropertyBinding uses delete[]
+ auto binding = new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::HasBoundFunction);
+ auto js = new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJSForBoundFunction();
Q_ASSERT(binding->jsExpression() == js);
Q_ASSERT(js->asBinding() == binding);
Q_UNUSED(js);
@@ -127,10 +119,25 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
}
+/*!
+ \fn bool QQmlPropertyBindingJS::hasDependencies()
+ \internal
+
+ Returns true if this binding has dependencies.
+ Dependencies can be either QProperty dependencies or dependencies of
+ the JS expression (aka activeGuards). Translations end up as a QProperty
+ dependency, so they do not need any special handling
+ Note that a QQmlPropertyBinding never stores qpropertyChangeTriggers.
+ */
+
+
void QQmlPropertyBindingJS::expressionChanged()
{
+ auto binding = asBinding();
+ if (!binding->propertyDataPtr)
+ return;
const auto currentTag = m_error.tag();
- if (currentTag & InEvaluationLoop) {
+ if (currentTag == InEvaluationLoop) {
QQmlError err;
auto location = QQmlJavaScriptExpression::sourceLocation();
err.setUrl(QUrl{location.sourceFile});
@@ -139,160 +146,142 @@ void QQmlPropertyBindingJS::expressionChanged()
const auto ctxt = context();
QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
if (engine)
- err.setDescription(asBinding()->createBindingLoopErrorDescription(QQmlEnginePrivate::get(engine)));
+ err.setDescription(asBinding()->createBindingLoopErrorDescription());
else
err.setDescription(QString::fromLatin1("Binding loop detected"));
err.setObject(asBinding()->target());
qmlWarning(this->scopeObject(), err);
return;
}
- m_error.setTag(currentTag | InEvaluationLoop);
- asBinding()->markDirtyAndNotifyObservers();
- m_error.setTag(currentTag);
+ m_error.setTag(InEvaluationLoop);
+ PendingBindingObserverList bindingObservers;
+ binding->evaluateRecursive(bindingObservers);
+ binding->notifyNonRecursive(bindingObservers);
+ m_error.setTag(NoTag);
}
-const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
-{
- return std::launder(reinterpret_cast<QQmlPropertyBinding const *>(reinterpret_cast<std::byte const*>(this) - sizeof(QQmlPropertyBinding) - jsExpressionOffsetLength()));
-}
-
-QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, bool hasBoundFunction)
+QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction)
: QPropertyBindingPrivate(mt,
- &QtPrivate::bindingFunctionVTable<QQmlPropertyBinding>,
+ bindingFunctionVTableForQQmlPropertyBinding(mt),
QPropertyBindingSourceLocation(), true)
{
static_assert (std::is_trivially_destructible_v<TargetData>);
static_assert (sizeof(TargetData) + sizeof(DeclarativeErrorCallback) <= sizeof(QPropertyBindingSourceLocation));
static_assert (alignof(TargetData) <= alignof(QPropertyBindingSourceLocation));
- new (&declarativeExtraData) TargetData {target, targetIndex, hasBoundFunction};
+ const auto state = hasBoundFunction ? TargetData::HasBoundFunction : TargetData::WithoutBoundFunction;
+ new (&declarativeExtraData) TargetData {target, targetIndex, state};
errorCallBack = bindingErrorCallback;
}
-bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
+static QtPrivate::QPropertyBindingData *bindingDataFromPropertyData(QUntypedPropertyData *dataPtr, QMetaType type)
{
- const auto ctxt = jsExpression()->context();
- QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
- if (!engine) {
- QPropertyBindingError error(QPropertyBindingError::EvaluationError);
- QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
- return false;
- }
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- ep->referenceScarceResources();
-
- bool isUndefined = false;
-
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue result(scope, hasBoundFunction()
- ? static_cast<QQmlPropertyBindingJSForBoundFunction *>(jsExpression())->evaluate(&isUndefined)
- : jsExpression()->evaluate(&isUndefined));
-
- ep->dereferenceScarceResources();
-
- if (jsExpression()->hasError()) {
- QPropertyBindingError error(QPropertyBindingError::UnknownError, jsExpression()->delayedError()->error().description());
- QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
- bindingErrorCallback(this);
- return false;
- }
+ // XXX Qt 7: We need a clean way to access the binding data
+ /* This function makes the (dangerous) assumption that if we could not get the binding data
+ from the binding storage, we must have been handed a QProperty.
+ This does hold for anything a user could write, as there the only ways of providing a bindable property
+ are to use the Q_X_BINDABLE macros, or to directly expose a QProperty.
+ As long as we can ensure that any "fancier" property we implement is not resettable, we should be fine.
+ We procede to calculate the address of the binding data pointer from the address of the data pointer
+ */
+ Q_ASSERT(dataPtr);
+ std::byte *qpropertyPointer = reinterpret_cast<std::byte *>(dataPtr);
+ qpropertyPointer += type.sizeOf();
+ constexpr auto alignment = alignof(QtPrivate::QPropertyBindingData *);
+ auto aligned = (quintptr(qpropertyPointer) + alignment - 1) & ~(alignment - 1); // ensure pointer alignment
+ return reinterpret_cast<QtPrivate::QPropertyBindingData *>(aligned);
+}
- int propertyType = metaType.id();
+void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void *dataPtr)
+{
+ const QQmlPropertyData *propertyData = nullptr;
+ QQmlPropertyData valueTypeData;
+ QQmlData *data = QQmlData::get(target(), false);
+ Q_ASSERT(data);
+ if (Q_UNLIKELY(!data->propertyCache))
+ data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
- switch (propertyType) {
- case QMetaType::Bool: {
- bool b;
- if (result->isBoolean())
- b = result->booleanValue();
- else
- b = result->toBoolean();
- if (b == *static_cast<bool *>(dataPtr))
- return false;
- *static_cast<bool *>(dataPtr) = b;
- return true;
- }
- case QMetaType::Int: {
- int i;
- if (result->isInteger())
- i = result->integerValue();
- else if (result->isNumber()) {
- i = QV4::StaticValue::toInteger(result->doubleValue());
- } else {
- break;
- }
- if (i == *static_cast<int *>(dataPtr))
- return false;
- *static_cast<int *>(dataPtr) = i;
- return true;
- }
- case QMetaType::Double:
- if (result->isNumber()) {
- double d = result->asDouble();
- if (d == *static_cast<double *>(dataPtr))
- return false;
- *static_cast<double *>(dataPtr) = d;
- return true;
- }
- break;
- case QMetaType::Float:
- if (result->isNumber()) {
- float d = float(result->asDouble());
- if (d == *static_cast<float *>(dataPtr))
- return false;
- *static_cast<float *>(dataPtr) = d;
- return true;
+ propertyData = data->propertyCache->property(targetIndex().coreIndex());
+ Q_ASSERT(propertyData);
+ Q_ASSERT(!targetIndex().hasValueTypeIndex());
+ QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData, nullptr);
+ // helper function for writing back value into dataPtr
+ // this is necessary for QObjectCompatProperty, which doesn't give us the "real" dataPtr
+ // if we don't write the correct value, we would otherwise set the default constructed value
+ auto writeBackCurrentValue = [&](QVariant &&currentValue) {
+ if (currentValue.metaType() != valueMetaType())
+ currentValue.convert(valueMetaType());
+ auto metaType = valueMetaType();
+ metaType.destruct(dataPtr);
+ metaType.construct(dataPtr, currentValue.constData());
+ };
+ if (prop.isResettable()) {
+ // Normally a reset would remove any existing binding; but now we need to keep the binding alive
+ // to handle the case where this binding becomes defined again
+ // We therefore detach the binding, call reset, and reattach again
+ const auto storage = qGetBindingStorage(target());
+ auto bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ QPropertyBindingDataPointer bindingDataPointer{bindingData};
+ auto firstObserver = takeObservers();
+ bindingData->d_ref() = 0;
+ if (firstObserver) {
+ bindingDataPointer.setObservers(firstObserver.ptr);
}
- break;
- case QMetaType::QString:
- if (result->isString()) {
- QString s = result->toQStringNoThrow();
- if (s == *static_cast<QString *>(dataPtr))
- return false;
- *static_cast<QString *>(dataPtr) = s;
- return true;
+ Q_ASSERT(!bindingData->hasBinding());
+ setIsUndefined(true);
+ //suspend binding evaluation state for reset and subsequent read
+ auto state = QtPrivate::suspendCurrentBindingStatus();
+ prop.reset(); // May re-allocate the bindingData
+ QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
+ QtPrivate::restoreBindingStatus(state);
+ writeBackCurrentValue(std::move(currentValue));
+
+ // Re-fetch binding data
+ bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ bindingDataPointer = QPropertyBindingDataPointer {bindingData};
+
+ // reattach the binding (without causing a new notification)
+ if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
+ qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n"
+ << "The old binding binding will be abandoned";
+ deref();
+ return;
}
- break;
- default:
- break;
+ // reset might have changed observers (?), so refresh firstObserver
+ firstObserver = bindingDataPointer.firstObserver();
+ bindingData->d_ref() = reinterpret_cast<quintptr>(this) | QtPrivate::QPropertyBindingData::BindingBit;
+ if (firstObserver)
+ prependObserver(firstObserver);
+ } else {
+ QQmlError qmlError;
+ auto location = jsExpression()->sourceLocation();
+ qmlError.setColumn(location.column);
+ qmlError.setLine(location.line);
+ qmlError.setUrl(QUrl {location.sourceFile});
+ const QString description = QStringLiteral(R"(QML %1: Unable to assign [undefined] to "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
+ qmlError.setDescription(description);
+ qmlError.setObject(target());
+ ep->warning(qmlError);
}
-
- QVariant resultVariant(scope.engine->toVariant(result, metaType.id()));
- resultVariant.convert(metaType);
- const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
- metaType.destruct(dataPtr);
- metaType.construct(dataPtr, resultVariant.constData());
- return hasChanged;
}
-QString QQmlPropertyBinding::createBindingLoopErrorDescription(QJSEnginePrivate *ep)
+QString QQmlPropertyBinding::createBindingLoopErrorDescription()
{
- QQmlPropertyData *propertyData = nullptr;
+ const QQmlPropertyData *propertyData = nullptr;
QQmlPropertyData valueTypeData;
QQmlData *data = QQmlData::get(target(), false);
Q_ASSERT(data);
- if (Q_UNLIKELY(!data->propertyCache)) {
- data->propertyCache = ep->cache(target()->metaObject());
- data->propertyCache->addref();
- }
+ if (Q_UNLIKELY(!data->propertyCache))
+ data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
+
propertyData = data->propertyCache->property(targetIndex().coreIndex());
Q_ASSERT(propertyData);
Q_ASSERT(!targetIndex().hasValueTypeIndex());
QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData, nullptr);
- return QStringLiteral(R"(QML %1: Binding loop detected for property "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
-}
-
-QObject *QQmlPropertyBinding::target()
-{
- return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->target;
-}
-
-QQmlPropertyIndex QQmlPropertyBinding::targetIndex()
-{
- return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->targetIndex;
-}
-
-bool QQmlPropertyBinding::hasBoundFunction()
-{
- return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->hasBoundFunction;
+ return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
}
void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
@@ -302,7 +291,6 @@ void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
auto engine = qmlEngine(target);
if (!engine)
return;
- auto ep = QQmlEnginePrivate::get(engine);
auto error = This->bindingError();
QQmlError qmlError;
@@ -312,21 +300,24 @@ void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
qmlError.setUrl(QUrl {location.sourceFile});
auto description = error.description();
if (error.type() == QPropertyBindingError::BindingLoop) {
- description = This->createBindingLoopErrorDescription(ep);
+ description = This->createBindingLoopErrorDescription();
}
qmlError.setDescription(description);
qmlError.setObject(target);
- ep->warning(qmlError);
+ QQmlEnginePrivate::get(engine)->warning(qmlError);
}
-QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(const QQmlPropertyData *pd, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+template<typename TranslateWithUnit>
+auto qQmlTranslationPropertyBindingCreateBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ TranslateWithUnit &&translateWithUnit)
{
- auto translationBinding = [compilationUnit, binding](const QMetaType &metaType, void *dataPtr) -> bool {
- // Create a dependency to the uiLanguage
- QJSEnginePrivate::get(compilationUnit->engine)->uiLanguage.value();
+ return [compilationUnit, translateWithUnit](QMetaType metaType, void *dataPtr) -> bool {
+ // Create a dependency to the translationLanguage
+ QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
- QVariant resultVariant(compilationUnit->bindingValueAsString(binding));
- if (metaType.id() != QMetaType::QString)
+ QVariant resultVariant(translateWithUnit(compilationUnit));
+ if (metaType != QMetaType::fromType<QString>())
resultVariant.convert(metaType);
const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
@@ -334,8 +325,38 @@ QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(const QQmlPropert
metaType.construct(dataPtr, resultVariant.constData());
return hasChanged;
};
+}
- return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding, QPropertyBindingSourceLocation());
+QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(
+ const QQmlPropertyData *pd,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding)
+{
+ auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
+ compilationUnit,
+ [binding](const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
+ return compilationUnit->bindingValueAsString(binding);
+ });
+
+ return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
+ QPropertyBindingSourceLocation());
+}
+
+QUntypedPropertyBinding QQmlTranslationPropertyBinding::create(
+ const QMetaType &propertyType,
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlTranslation &translationData)
+{
+ auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
+ compilationUnit,
+ [translationData](
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
+ Q_UNUSED(compilationUnit);
+ return translationData.translate();
+ });
+
+ return QUntypedPropertyBinding(propertyType, translationBinding,
+ QPropertyBindingSourceLocation());
}
QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(bool *isUndefined)
@@ -345,7 +366,7 @@ QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(bool *isUndef
const QV4::Value *argv = nullptr;
const QV4::Value *thisObject = nullptr;
QV4::BoundFunction *b = nullptr;
- if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
+ if ((b = m_boundFunction.as<QV4::BoundFunction>())) {
QV4::Heap::MemberData *args = b->boundArgs();
if (args) {
argc = args->values.size;
@@ -354,9 +375,9 @@ QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(bool *isUndef
thisObject = &b->d()->boundThis;
}
QV4::Scope scope(v4);
- QV4::JSCallData jsCall(scope, argc, argv, thisObject);
+ QV4::JSCallData jsCall(thisObject, argv, argc);
- return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined);
+ return QQmlJavaScriptExpression::evaluate(jsCall.callData(scope), isUndefined);
}
QT_END_NAMESPACE