aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlbinding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlbinding.cpp')
-rw-r--r--src/qml/qml/qqmlbinding.cpp628
1 files changed, 361 insertions, 267 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 69ac63b0ad..47f8e5c429 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -1,47 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 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 "qqmlbinding_p.h"
-#include "qqml.h"
#include "qqmlcontext.h"
-#include "qqmlinfo.h"
#include "qqmldata_p.h"
#include <private/qqmldebugserviceinterfaces_p.h>
@@ -67,9 +29,12 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtqml, QQmlBinding_entry, const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlBinding_exit)
+
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
{
- QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+ QQmlBinding *b = newBinding(property);
if (ctxt && !ctxt->isValid())
return b;
@@ -125,7 +90,7 @@ QQmlBinding *QQmlBinding::create(
const QQmlPropertyData *property, const QString &str, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber)
{
- QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+ QQmlBinding *b = newBinding(property);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
@@ -140,7 +105,14 @@ QQmlBinding *QQmlBinding::create(
const QQmlPropertyData *property, QV4::Function *function, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope)
{
- QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+ return create(property ? property->propType() : QMetaType(), function, obj, ctxt, scope);
+}
+
+QQmlBinding *QQmlBinding::create(QMetaType propertyType, QV4::Function *function, QObject *obj,
+ const QQmlRefPointer<QQmlContextData> &ctxt,
+ QV4::ExecutionContext *scope)
+{
+ QQmlBinding *b = newBinding(propertyType);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
@@ -168,7 +140,7 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
// Check for a binding update loop
if (Q_UNLIKELY(updatingFlag())) {
- QQmlPropertyData *d = nullptr;
+ const QQmlPropertyData *d = nullptr;
QQmlPropertyData vtd;
getPropertyData(&d, &vtd);
Q_ASSERT(d);
@@ -211,81 +183,44 @@ QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
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);
}
-
-// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
-// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
-// expression for the switch for the compiler to generate the optimal code, but
-// qMetaTypeId<QQmlBinding *>() needs to be used for the ID. So QQmlBinding::newBinding uses that
-// to instantiate this class.
-class QQmlBindingBinding: public QQmlBinding
-{
-protected:
- void doUpdate(const DeleteWatcher &,
- QQmlPropertyData::WriteFlags flags, QV4::Scope &) override final
- {
- Q_ASSERT(!m_targetIndex.hasValueTypeIndex());
- QQmlPropertyData *pd = nullptr;
- getPropertyData(&pd, nullptr);
- QQmlBinding *thisPtr = this;
- pd->writeProperty(m_target.data(), &thisPtr, flags);
- }
-};
-
-// For any target that's not a binding, we have a common doUpdate. However, depending on the type
-// of the target property, there is a specialized write method.
-class QQmlNonbindingBinding: public QQmlBinding
+template<int StaticPropType>
+class GenericBinding: public QQmlBinding
{
protected:
- void doUpdate(const DeleteWatcher &watcher,
- QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override
+ // Returns true if successful, false if an error description was set on expression
+ Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) override final
{
- auto ep = QQmlEnginePrivate::get(scope.engine);
- ep->referenceScarceResources();
-
- bool isUndefined = false;
-
- QV4::ScopedValue result(scope, evaluate(&isUndefined));
-
- bool error = false;
- if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
- error = !write(result, isUndefined, flags);
-
- if (!watcher.wasDeleted()) {
+ const QQmlPropertyData *pd;
+ QQmlPropertyData vpd;
+ getPropertyData(&pd, &vpd);
+ Q_ASSERT(pd);
- if (error) {
- delayedError()->setErrorLocation(sourceLocation());
- delayedError()->setErrorObject(m_target.data());
- }
+ if (isUndefined || vpd.isValid())
+ return slowWrite(*pd, vpd, result, type, isUndefined, flags);
- if (hasError()) {
- if (!delayedError()->addError(ep)) ep->warning(this->error(engine()));
- } else {
- clearError();
- }
+ if ((StaticPropType == QMetaType::UnknownType && pd->propType() == type)
+ || StaticPropType == type.id()) {
+ Q_ASSERT(targetObject());
+ return pd->writeProperty(targetObject(), result, flags);
}
- ep->dereferenceScarceResources();
+ // If the type didn't match, we need to do JavaScript conversion. This should be rare.
+ return write(engine()->handle()->metaTypeToJS(type, result), isUndefined, flags);
}
- virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
-};
-
-template<int StaticPropType>
-class GenericBinding: public QQmlNonbindingBinding
-{
-protected:
// Returns true if successful, false if an error description was set on expression
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
QQmlPropertyData::WriteFlags flags) override final
{
Q_ASSERT(targetObject());
- QQmlPropertyData *pd;
+ const QQmlPropertyData *pd;
QQmlPropertyData vpd;
getPropertyData(&pd, &vpd);
Q_ASSERT(pd);
@@ -305,7 +240,7 @@ protected:
if (result.isInteger())
return doStore<int>(result.integerValue(), pd, flags);
else if (result.isNumber()) {
- return doStore<int>(QV4::StaticValue::toInteger(result.doubleValue()), pd, flags);
+ return doStore<int>(result.toInt32(), pd, flags);
}
break;
case QMetaType::Double:
@@ -322,7 +257,7 @@ protected:
break;
default:
if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType()->metaType == pd->propType()) {
+ if (vtw->d()->metaType() == pd->propType()) {
return vtw->write(m_target.data(), pd->coreIndex());
}
}
@@ -341,19 +276,19 @@ protected:
}
};
-class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
+class QQmlTranslationBinding : public GenericBinding<QMetaType::QString>, public QPropertyObserver {
public:
- QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
+ : QPropertyObserver(&QQmlTranslationBinding::onLanguageChange)
{
setCompilationUnit(compilationUnit);
- m_binding = binding;
+ setSource(QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage);
}
- QQmlSourceLocation sourceLocation() const override final
- {
- return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
- }
+ virtual QString bindingValue() const = 0;
+ static void onLanguageChange(QPropertyObserver *observer, QUntypedPropertyData *)
+ { static_cast<QQmlTranslationBinding *>(observer)->update(); }
void doUpdate(const DeleteWatcher &watcher,
QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
@@ -364,11 +299,11 @@ public:
if (!isAddedToObject() || hasError())
return;
- const QString result = m_compilationUnit->bindingValueAsString(m_binding);
+ const QString result = this->bindingValue();
Q_ASSERT(targetObject());
- QQmlPropertyData *pd;
+ const QQmlPropertyData *pd;
QQmlPropertyData vpd;
getPropertyData(&pd, &vpd);
Q_ASSERT(pd);
@@ -381,9 +316,56 @@ public:
}
bool hasDependencies() const override final { return true; }
+};
-private:
+class QQmlTranslationBindingFromBinding : public QQmlTranslationBinding
+{
const QV4::CompiledData::Binding *m_binding;
+
+public:
+ QQmlTranslationBindingFromBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding)
+ : QQmlTranslationBinding(compilationUnit), m_binding(binding)
+ {
+ }
+
+ QString bindingValue() const override
+ {
+ return this->m_compilationUnit->bindingValueAsString(m_binding);
+ }
+
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line(),
+ m_binding->valueLocation.column());
+ }
+};
+
+class QQmlTranslationBindingFromTranslationInfo : public QQmlTranslationBinding
+{
+ QQmlTranslation m_translationData;
+
+ quint16 m_line;
+ quint16 m_column;
+
+public:
+ QQmlTranslationBindingFromTranslationInfo(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlTranslation &translationData, quint16 line, quint16 column)
+ : QQmlTranslationBinding(compilationUnit),
+ m_translationData(translationData),
+ m_line(line),
+ m_column(column)
+ {
+ }
+
+ virtual QString bindingValue() const override { return m_translationData.translate(); }
+
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_line, m_column);
+ }
};
QQmlBinding *QQmlBinding::createTranslationBinding(
@@ -391,29 +373,106 @@ QQmlBinding *QQmlBinding::createTranslationBinding(
const QV4::CompiledData::Binding *binding, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt)
{
- QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
+ QQmlTranslationBinding *b = new QQmlTranslationBindingFromBinding(unit, binding);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
b->setScopeObject(obj);
-
+#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
if (QQmlDebugTranslationService *service
= QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
- service->foundTranslationBinding(b, obj, ctxt);
+ service->foundTranslationBinding(
+ TranslationBindingInformation::create(unit, binding, b->scopeObject(), ctxt));
}
+#endif
+ return b;
+}
+QQmlBinding *QQmlBinding::createTranslationBinding(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlRefPointer<QQmlContextData> &ctxt, const QString &propertyName,
+ const QQmlTranslation &translationData, const QQmlSourceLocation &location, QObject *obj)
+{
+ QQmlTranslationBinding *b = new QQmlTranslationBindingFromTranslationInfo(
+ unit, translationData, location.column, location.line);
+
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
+
+#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
+ QString originString;
+ if (QQmlDebugTranslationService *service =
+ QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
+ service->foundTranslationBinding({ unit, b->scopeObject(), ctxt,
+
+ propertyName, translationData,
+
+ location.line, location.column });
+ }
+#else
+ Q_UNUSED(propertyName)
+#endif
return b;
}
+bool QQmlBinding::slowWrite(
+ const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const void *result,
+ QMetaType resultType, bool isUndefined, QQmlPropertyData::WriteFlags flags)
+{
+ // The logic in this method is obscure. It follows what the other slowWrite does, minus the type
+ // conversions and the checking for binding functions. If you're writing a C++ type, and
+ // you're passing a binding function wrapped into QJSValue, you probably want it to be assigned.
+
+ if (hasError())
+ return false;
+
+ QQmlEngine *qmlEngine = engine();
+ const QMetaType metaType = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
+ QQmlJavaScriptExpression::DeleteWatcher watcher(this);
+
+ if (core.isVarProperty()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data());
+ Q_ASSERT(vmemo);
+ vmemo->setVMEProperty(core.coreIndex(),
+ qmlEngine->handle()->metaTypeToJS(resultType, result));
+ } else if (isUndefined && core.isResettable()) {
+ void *args[] = { nullptr };
+ QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
+ } else if (isUndefined && metaType == QMetaType::fromType<QVariant>()) {
+ QQmlPropertyPrivate::writeValueProperty(
+ m_target.data(), core, valueTypeData, QVariant(), context(), flags);
+ } else if (metaType == QMetaType::fromType<QJSValue>()) {
+ QQmlPropertyPrivate::writeValueProperty(
+ m_target.data(), core, valueTypeData,
+ QVariant(resultType, result), context(), flags);
+ } else if (isUndefined) {
+ const char *name = metaType.name();
+ const QString typeName = name
+ ? QString::fromUtf8(name)
+ : QStringLiteral("[unknown property type]");
+ delayedError()->setErrorDescription(
+ QStringLiteral("Unable to assign [undefined] to ") + typeName);
+ return false;
+ } else if (!QQmlPropertyPrivate::writeValueProperty(
+ m_target.data(), core, valueTypeData, QVariant(resultType, result),
+ context(), flags)) {
+ if (watcher.wasDeleted())
+ return true;
+ handleWriteError(result, resultType, metaType);
+ return false;
+ }
+
+ return true;
+}
+
Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
const QQmlPropertyData &valueTypeData,
const QV4::Value &result,
bool isUndefined, QQmlPropertyData::WriteFlags flags)
{
- QQmlEngine *qmlEngine = engine();
- QV4::ExecutionEngine *v4engine = qmlEngine->handle();
-
- const int type = valueTypeData.isValid() ? valueTypeData.propType().id() : core.propType().id();
+ const QMetaType metaType = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
+ const int type = metaType.id();
QQmlJavaScriptExpression::DeleteWatcher watcher(this);
@@ -422,13 +481,20 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (isUndefined) {
} else if (core.isQList()) {
- value = v4engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
+ if (core.propType().flags() & QMetaType::IsQmlList)
+ value = QV4::ExecutionEngine::toVariant(result, QMetaType::fromType<QList<QObject*>>());
+ else
+ value = QV4::ExecutionEngine::toVariant(result, core.propType());
} else if (result.isNull() && core.isQObject()) {
value = QVariant::fromValue((QObject *)nullptr);
- } else if (core.propType().id() == qMetaTypeId<QList<QUrl> >()) {
- value = QQmlPropertyPrivate::urlSequence(v4engine->toVariant(result, qMetaTypeId<QList<QUrl>>()));
- } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
- value = v4engine->toVariant(result, type);
+ } else if (core.propType() == QMetaType::fromType<QList<QUrl>>()) {
+ const QVariant resultVariant
+ = QV4::ExecutionEngine::toVariant(result, QMetaType::fromType<QList<QUrl>>());
+ value = QVariant::fromValue(QQmlPropertyPrivate::resolveUrlsOnAssignment()
+ ? QQmlPropertyPrivate::urlSequence(resultVariant, context())
+ : QQmlPropertyPrivate::urlSequence(resultVariant));
+ } else if (!isVarProperty && metaType != QMetaType::fromType<QJSValue>()) {
+ value = QV4::ExecutionEngine::toVariant(result, metaType);
}
if (hasError()) {
@@ -445,12 +511,13 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data());
Q_ASSERT(vmemo);
vmemo->setVMEProperty(core.coreIndex(), result);
- } else if (isUndefined && core.isResettable()) {
- void *args[] = { nullptr };
- QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
- } else if (isUndefined && type == qMetaTypeId<QVariant>()) {
+ } else if (isUndefined
+ && (valueTypeData.isValid() ? valueTypeData.isResettable() : core.isResettable())) {
+ QQmlPropertyPrivate::resetValueProperty(
+ m_target.data(), core, valueTypeData, context(), flags);
+ } else if (isUndefined && type == QMetaType::QVariant) {
QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags);
- } else if (type == qMetaTypeId<QJSValue>()) {
+ } else if (metaType == QMetaType::fromType<QJSValue>()) {
const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
if (f && f->isBinding()) {
delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
@@ -476,41 +543,45 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (watcher.wasDeleted())
return true;
+ handleWriteError(value.constData(), value.metaType(), metaType);
+ return false;
+ }
- const char *valueType = nullptr;
- const char *propertyType = nullptr;
-
- const int userType = value.userType();
- if (userType == QMetaType::QObjectStar) {
- if (QObject *o = *(QObject *const *)value.constData()) {
- valueType = o->metaObject()->className();
+ return true;
+}
- QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(qmlEngine), type);
- if (!propertyMetaObject.isNull())
- propertyType = propertyMetaObject.className();
- }
- } else if (userType != QMetaType::UnknownType) {
- if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar)
- valueType = "null";
- else
- valueType = QMetaType(userType).name();
+void QQmlBinding::handleWriteError(const void *result, QMetaType resultType, QMetaType metaType)
+{
+ const char *valueType = nullptr;
+ const char *propertyType = nullptr;
+
+ if (resultType.flags() & QMetaType::PointerToQObject) {
+ if (QObject *o = *(QObject *const *)result) {
+ valueType = o->metaObject()->className();
+ QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(metaType);
+ if (!propertyMetaObject.isNull())
+ propertyType = propertyMetaObject.className();
+ }
+ } else if (resultType.isValid()) {
+ if (resultType == QMetaType::fromType<std::nullptr_t>()
+ || resultType == QMetaType::fromType<void *>()) {
+ valueType = "null";
+ } else {
+ valueType = resultType.name();
}
-
- if (!valueType)
- valueType = "undefined";
- if (!propertyType)
- propertyType = QMetaType(type).name();
- if (!propertyType)
- propertyType = "[unknown property type]";
-
- delayedError()->setErrorDescription(QLatin1String("Unable to assign ") +
- QLatin1String(valueType) +
- QLatin1String(" to ") +
- QLatin1String(propertyType));
- return false;
}
- return true;
+ if (!valueType)
+ valueType = "undefined";
+ if (!propertyType)
+ propertyType = metaType.name();
+ if (!propertyType)
+ propertyType = "[unknown property type]";
+
+ delayedError()->setErrorDescription(QStringLiteral("Unable to assign ")
+ + QString::fromUtf8(valueType)
+ + QStringLiteral(" to ")
+ + QString::fromUtf8(propertyType));
}
QVariant QQmlBinding::evaluate()
@@ -526,7 +597,7 @@ QVariant QQmlBinding::evaluate()
ep->dereferenceScarceResources();
- return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >());
+ return QV4::ExecutionEngine::toVariant(result, QMetaType::fromType<QList<QObject*> >());
}
void QQmlBinding::expressionChanged()
@@ -544,12 +615,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
const bool wasEnabled = enabledFlag();
setEnabledFlag(e);
setNotifyOnValueChanged(e);
-
- m_nextBinding.setTag(m_nextBinding.tag().setFlag(CanUseAccessor)); // Always use accessors, only not when:
- if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) {
- if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex))
- m_nextBinding.setTag(m_nextBinding.tag().setFlag(CanUseAccessor, false));
- }
+ updateCanUseAccessor();
if (e && !wasEnabled)
update(flags);
@@ -560,85 +626,6 @@ QString QQmlBinding::expression() const
return QStringLiteral("function() { [native code] }");
}
-void QQmlBinding::setTarget(const QQmlProperty &prop)
-{
- auto pd = QQmlPropertyPrivate::get(prop);
- setTarget(prop.object(), pd->core, &pd->valueTypeData);
-}
-
-bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
-{
- m_target = object;
-
- if (!object) {
- m_targetIndex = QQmlPropertyIndex();
- return false;
- }
-
- int coreIndex = core.coreIndex();
- int valueTypeIndex = valueType ? valueType->coreIndex() : -1;
- for (bool isAlias = core.isAlias(); isAlias; ) {
- QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
-
- int aValueTypeIndex;
- if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
- // can't resolve id (yet)
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
- }
- if (valueTypeIndex == -1)
- valueTypeIndex = aValueTypeIndex;
-
- QQmlData *data = QQmlData::get(object, false);
- if (!data || !data->propertyCache) {
- m_target = nullptr;
- m_targetIndex = QQmlPropertyIndex();
- return false;
- }
- QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
- Q_ASSERT(propertyData);
-
- m_target = object;
- isAlias = propertyData->isAlias();
- coreIndex = propertyData->coreIndex();
- }
- m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex);
-
- QQmlData *data = QQmlData::get(m_target.data(), true);
- if (!data->propertyCache) {
- data->propertyCache = QQmlEnginePrivate::get(engine())->cache(m_target->metaObject());
- data->propertyCache->addref();
- }
-
- return true;
-}
-
-void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
-{
- Q_ASSERT(propertyData);
-
- QQmlData *data = QQmlData::get(m_target.data(), false);
- Q_ASSERT(data);
-
- if (Q_UNLIKELY(!data->propertyCache)) {
- data->propertyCache = QQmlEnginePrivate::get(engine())->cache(m_target->metaObject());
- data->propertyCache->addref();
- }
-
- *propertyData = data->propertyCache->property(m_targetIndex.coreIndex());
- Q_ASSERT(*propertyData);
-
- if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
- const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType((*propertyData)->propType());
- Q_ASSERT(valueTypeMetaObject);
- QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
- valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
- valueTypeData->setPropType(vtProp.metaType());
- valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
- }
-}
-
QVector<QQmlProperty> QQmlBinding::dependencies() const
{
QVector<QQmlProperty> dependencies;
@@ -660,33 +647,132 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
for (int i = 0; i < senderMeta->propertyCount(); i++) {
QMetaProperty property = senderMeta->property(i);
if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
- dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
+ dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(property.name())));
}
}
}
+ for (auto trigger = qpropertyChangeTriggers; trigger; trigger = trigger->next) {
+ QMetaProperty prop = trigger->property();
+ if (prop.isValid())
+ dependencies.push_back(QQmlProperty(trigger->target, QString::fromUtf8(prop.name())));
+ }
+
return dependencies;
}
bool QQmlBinding::hasDependencies() const
{
- return !activeGuards.isEmpty() || translationsCaptured() || qpropertyChangeTriggers;
+ return !activeGuards.isEmpty() || qpropertyChangeTriggers;
}
-class QObjectPointerBinding: public QQmlNonbindingBinding
+void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope)
+{
+ auto ep = QQmlEnginePrivate::get(scope.engine);
+ ep->referenceScarceResources();
+
+ bool error = false;
+ auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
+ const QV4::Function *v4Function = function();
+ if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
+ const auto returnType = v4Function->aotCompiledFunction.types[0];
+ if (returnType == QMetaType::fromType<QVariant>()) {
+ QVariant result;
+ const bool isUndefined = !evaluate(&result, returnType);
+ if (canWrite())
+ error = !write(result.data(), result.metaType(), isUndefined, flags);
+ } else {
+ const auto size = returnType.sizeOf();
+ if (Q_LIKELY(size > 0)) {
+ Q_ALLOCA_VAR(void, result, size);
+ if (returnType.flags() & QMetaType::NeedsConstruction)
+ returnType.construct(result);
+ const bool isUndefined = !evaluate(result, returnType);
+ if (canWrite())
+ error = !write(result, returnType, isUndefined, flags);
+ if (returnType.flags() & QMetaType::NeedsDestruction)
+ returnType.destruct(result);
+ } else if (canWrite()) {
+ error = !write(QV4::Encode::undefined(), true, flags);
+ }
+ }
+ } else {
+ bool isUndefined = false;
+ QV4::ScopedValue result(scope, evaluate(&isUndefined));
+ if (canWrite())
+ error = !write(result, isUndefined, flags);
+ }
+
+ if (!watcher.wasDeleted()) {
+
+ if (error) {
+ delayedError()->setErrorLocation(sourceLocation());
+ delayedError()->setErrorObject(m_target.data());
+ }
+
+ if (hasError()) {
+ if (!delayedError()->addError(ep)) ep->warning(this->error(engine()));
+ } else {
+ clearError();
+ }
+ }
+
+ ep->dereferenceScarceResources();
+}
+
+class QObjectPointerBinding: public QQmlBinding
{
QQmlMetaObject targetMetaObject;
public:
- QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType)
- : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType))
+ QObjectPointerBinding(QMetaType propertyType)
+ : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(propertyType))
{}
protected:
+ Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) override final
+ {
+ const QQmlPropertyData *pd;
+ QQmlPropertyData vtpd;
+ getPropertyData(&pd, &vtpd);
+ if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
+ return slowWrite(*pd, vtpd, result, type, isUndefined, flags);
+
+ // Check if the result is a QObject:
+ QObject *resultObject = nullptr;
+ QQmlMetaObject resultMo;
+ const auto typeFlags = type.flags();
+ if (!result || ((typeFlags & QMetaType::IsPointer) && !*static_cast<void **>(result))) {
+ // Special case: we can always write a nullptr. Don't bother checking anything else.
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ } else if (typeFlags & QMetaType::PointerToQObject) {
+ resultObject = *static_cast<QObject **>(result);
+ if (!resultObject)
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ if (QQmlData *ddata = QQmlData::get(resultObject, false))
+ resultMo = ddata->propertyCache;
+ if (resultMo.isNull())
+ resultMo = resultObject->metaObject();
+ } else if (type == QMetaType::fromType<QVariant>()) {
+ const QVariant value = *static_cast<QVariant *>(result);
+ resultMo = QQmlPropertyPrivate::rawMetaObjectForType(value.metaType());
+ if (resultMo.isNull())
+ return slowWrite(*pd, vtpd, result, type, isUndefined, flags);
+ resultObject = *static_cast<QObject *const *>(value.constData());
+ } else {
+ return slowWrite(*pd, vtpd, result, type, isUndefined, flags);
+ }
+
+ return compareAndSet(resultMo, resultObject, pd, flags, [&]() {
+ return slowWrite(*pd, vtpd, result, type, isUndefined, flags);
+ });
+ }
+
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
QQmlPropertyData::WriteFlags flags) override final
{
- QQmlPropertyData *pd;
+ const QQmlPropertyData *pd;
QQmlPropertyData vtpd;
getPropertyData(&pd, &vtpd);
if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
@@ -708,10 +794,8 @@ protected:
resultMo = resultObject->metaObject();
}
} else if (auto variant = result.as<QV4::VariantObject>()) {
- QVariant value = variant->d()->data();
- QQmlEngine *qmlEngine = engine();
- resultMo = QQmlPropertyPrivate::rawMetaObjectForType(
- qmlEngine ? QQmlEnginePrivate::get(qmlEngine) : nullptr, value.userType());
+ const QVariant value = variant->d()->data();
+ resultMo = QQmlPropertyPrivate::rawMetaObjectForType(value.metaType());
if (resultMo.isNull())
return slowWrite(*pd, vtpd, result, isUndefined, flags);
resultObject = *static_cast<QObject *const *>(value.constData());
@@ -719,7 +803,18 @@ protected:
return slowWrite(*pd, vtpd, result, isUndefined, flags);
}
- // Compare & set:
+ return compareAndSet(resultMo, resultObject, pd, flags, [&]() {
+ return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ });
+ }
+
+private:
+ using QQmlBinding::slowWrite;
+
+ template<typename SlowWrite>
+ bool compareAndSet(const QQmlMetaObject &resultMo, QObject *resultObject, const QQmlPropertyData *pd,
+ QQmlPropertyData::WriteFlags flags, const SlowWrite &slowWrite) const
+ {
if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) {
return pd->writeProperty(targetObject(), &resultObject, flags);
} else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) {
@@ -728,23 +823,22 @@ protected:
// the property type.
return pd->writeProperty(targetObject(), &resultObject, flags);
} else {
- return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ return slowWrite();
}
}
};
-QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property)
+QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property)
{
- if (property && property->isQObject())
- return new QObjectPointerBinding(engine, property->propType().id());
-
- const int type = property ? property->propType().id() : QMetaType::UnknownType;
+ return newBinding(property ? property->propType() : QMetaType());
+}
- if (type == qMetaTypeId<QQmlBinding *>()) {
- return new QQmlBindingBinding;
- }
+QQmlBinding *QQmlBinding::newBinding(QMetaType propertyType)
+{
+ if (propertyType.flags() & QMetaType::PointerToQObject)
+ return new QObjectPointerBinding(propertyType);
- switch (type) {
+ switch (propertyType.id()) {
case QMetaType::Bool:
return new GenericBinding<QMetaType::Bool>;
case QMetaType::Int: