aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlbinding.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-03-21 20:42:38 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-03-21 20:42:47 +0100
commit6767114285db9d0e16dc278d08f231e8561546b4 (patch)
tree0945902a2242fd7ec0a1f7fd3e6acbb769e723bd /src/qml/qml/qqmlbinding.cpp
parentee076afedccbe1d37306a7972051f84eb036d655 (diff)
parentc32b109e9dea44c6775c2dbf8f164870c1dc8971 (diff)
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Diffstat (limited to 'src/qml/qml/qqmlbinding.cpp')
-rw-r--r--src/qml/qml/qqmlbinding.cpp227
1 files changed, 185 insertions, 42 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 62288a5845..a949df4968 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -51,9 +51,11 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4jscall_p.h>
#include <QVariant>
#include <QtCore/qdebug.h>
+#include <QVector>
QT_BEGIN_NAMESPACE
@@ -69,7 +71,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
return b;
QString url;
- QV4::Function *runtimeFunction = 0;
+ QV4::Function *runtimeFunction = nullptr;
QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
@@ -83,7 +85,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
b->setScopeObject(obj ? obj : scriptPrivate->scope);
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(b->context()->engine)->v4engine();
+ QV4::ExecutionEngine *v4 = b->context()->engine->handle();
if (runtimeFunction) {
QV4::Scope scope(v4);
QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, b->scopeObject()));
@@ -96,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
return b;
}
+QQmlSourceLocation QQmlBinding::sourceLocation() const
+{
+ if (m_sourceLocation)
+ return *m_sourceLocation;
+ return QQmlJavaScriptExpression::sourceLocation();
+}
+
+void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
+{
+ if (m_sourceLocation)
+ delete m_sourceLocation;
+ m_sourceLocation = new QQmlSourceLocation(location);
+}
+
+
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
{
@@ -127,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function
QQmlBinding::~QQmlBinding()
{
+ delete m_sourceLocation;
}
void QQmlBinding::setNotifyOnValueChanged(bool v)
@@ -149,7 +167,7 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
QQmlPropertyData vtd;
getPropertyData(&d, &vtd);
Q_ASSERT(d);
- QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, 0);
+ QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, nullptr);
QQmlAbstractBinding::printBindingLoopError(p);
return;
}
@@ -157,19 +175,41 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
DeleteWatcher watcher(this);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
- QV4::Scope scope(ep->v4engine());
+ QQmlEngine *engine = context()->engine;
+ QV4::Scope scope(engine->handle());
if (canUseAccessor())
flags.setFlag(QQmlPropertyData::BypassInterceptor);
- QQmlBindingProfiler prof(ep->profiler, function());
+ QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function());
doUpdate(watcher, flags, scope);
if (!watcher.wasDeleted())
setUpdatingFlag(false);
}
+QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
+{
+ QV4::ExecutionEngine *v4 = context()->engine->handle();
+ int argc = 0;
+ const QV4::Value *argv = nullptr;
+ const QV4::Value *thisObject = nullptr;
+ QV4::BoundFunction *b = nullptr;
+ if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
+ QV4::Heap::MemberData *args = b->boundArgs();
+ if (args) {
+ argc = args->values.size;
+ argv = args->values.data();
+ }
+ thisObject = &b->d()->boundThis;
+ }
+ QV4::Scope scope(v4);
+ QV4::JSCallData jsCall(scope, argc, argv, thisObject);
+
+ return QQmlJavaScriptExpression::evaluate(jsCall.callData(), 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
@@ -179,7 +219,7 @@ class QQmlBindingBinding: public QQmlBinding
{
protected:
void doUpdate(const DeleteWatcher &,
- QQmlPropertyData::WriteFlags flags, QV4::Scope &) Q_DECL_OVERRIDE Q_DECL_FINAL
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &) override final
{
Q_ASSERT(!m_targetIndex.hasValueTypeIndex());
QQmlPropertyData *pd = nullptr;
@@ -195,19 +235,18 @@ class QQmlNonbindingBinding: public QQmlBinding
{
protected:
void doUpdate(const DeleteWatcher &watcher,
- QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) Q_DECL_OVERRIDE Q_DECL_FINAL
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override
{
auto ep = QQmlEnginePrivate::get(scope.engine);
ep->referenceScarceResources();
bool isUndefined = false;
- QV4::ScopedCallData callData(scope);
- QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
+ QV4::ScopedValue result(scope, evaluate(&isUndefined));
bool error = false;
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
- error = !write(scope.result, isUndefined, flags);
+ error = !write(result, isUndefined, flags);
if (!watcher.wasDeleted()) {
@@ -237,7 +276,7 @@ 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) Q_DECL_OVERRIDE Q_DECL_FINAL
+ QQmlPropertyData::WriteFlags flags) override final
{
Q_ASSERT(targetObject());
@@ -277,7 +316,7 @@ protected:
break;
default:
if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType->typeId == pd->propType()) {
+ if (vtw->d()->valueType->metaType.id() == pd->propType()) {
return vtw->write(m_target.data(), pd->coreIndex());
}
}
@@ -296,13 +335,69 @@ protected:
}
};
+class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
+public:
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+ {
+ setCompilationUnit(compilationUnit);
+ m_binding = binding;
+ }
+
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
+ }
+
+
+ void doUpdate(const DeleteWatcher &watcher,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
+ {
+ if (watcher.wasDeleted())
+ return;
+
+ if (!isAddedToObject() || hasError())
+ return;
+
+ const QString result = m_binding->valueAsString(m_compilationUnit.data());
+
+ Q_ASSERT(targetObject());
+
+ QQmlPropertyData *pd;
+ QQmlPropertyData vpd;
+ getPropertyData(&pd, &vpd);
+ Q_ASSERT(pd);
+ if (pd->propType() == QMetaType::QString) {
+ doStore(result, pd, flags);
+ } else {
+ QV4::ScopedString value(scope, scope.engine->newString(result));
+ slowWrite(*pd, vpd, value, /*isUndefined*/false, flags);
+ }
+ }
+
+ bool hasDependencies() const override final { return true; }
+
+private:
+ const QV4::CompiledData::Binding *m_binding;
+};
+
+QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
+{
+ QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
+
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
+
+ return b;
+}
+
Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
const QQmlPropertyData &valueTypeData,
const QV4::Value &result,
bool isUndefined, QQmlPropertyData::WriteFlags flags)
{
QQmlEngine *engine = context()->engine;
- QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine);
+ QV4::ExecutionEngine *v4engine = engine->handle();
int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
@@ -313,13 +408,13 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (isUndefined) {
} else if (core.isQList()) {
- value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >());
+ value = v4engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
} else if (result.isNull() && core.isQObject()) {
- value = QVariant::fromValue((QObject *)0);
+ value = QVariant::fromValue((QObject *)nullptr);
} else if (core.propType() == qMetaTypeId<QList<QUrl> >()) {
- value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context());
+ value = QQmlPropertyPrivate::resolvedUrlSequence(v4engine->toVariant(result, qMetaTypeId<QList<QUrl> >()), context());
} else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
- value = QV8Engine::getV4(v8engine)->toVariant(result, type);
+ value = v4engine->toVariant(result, type);
}
if (hasError()) {
@@ -337,7 +432,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
Q_ASSERT(vmemo);
vmemo->setVMEProperty(core.coreIndex(), result);
} else if (isUndefined && core.isResettable()) {
- void *args[] = { 0 };
+ void *args[] = { nullptr };
QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
} else if (isUndefined && type == qMetaTypeId<QVariant>()) {
QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags);
@@ -348,7 +443,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
return false;
}
QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue(
- QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())),
+ QJSValue(v4engine, result.asReturnedValue())),
context(), flags);
} else if (isUndefined) {
const QLatin1String typeName(QMetaType::typeName(type)
@@ -368,8 +463,8 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (watcher.wasDeleted())
return true;
- const char *valueType = 0;
- const char *propertyType = 0;
+ const char *valueType = nullptr;
+ const char *propertyType = nullptr;
const int userType = value.userType();
if (userType == QMetaType::QObjectStar) {
@@ -406,27 +501,30 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
QVariant QQmlBinding::evaluate()
{
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
+ QQmlEngine *engine = context()->engine;
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
ep->referenceScarceResources();
bool isUndefined = false;
- QV4::Scope scope(ep->v4engine());
- QV4::ScopedCallData callData(scope);
- QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
ep->dereferenceScarceResources();
- return scope.engine->toVariant(scope.result, qMetaTypeId<QList<QObject*> >());
+ return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >());
}
QString QQmlBinding::expressionIdentifier() const
{
- auto f = function();
- QString url = f->sourceFile();
- quint16 lineNumber = f->compiledFunction->location.line;
- quint16 columnNumber = f->compiledFunction->location.column;
- return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber));
+ if (auto f = function()) {
+ QString url = f->sourceFile();
+ quint16 lineNumber = f->compiledFunction->location.line;
+ quint16 columnNumber = f->compiledFunction->location.column;
+ return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber));
+ }
+
+ return QStringLiteral("[native code]");
}
void QQmlBinding::expressionChanged()
@@ -441,6 +539,7 @@ void QQmlBinding::refresh()
void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
{
+ const bool wasEnabled = enabledFlag();
setEnabledFlag(e);
setNotifyOnValueChanged(e);
@@ -450,13 +549,13 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
m_nextBinding.clearFlag2();
}
- if (e)
+ if (e && !wasEnabled)
update(flags);
}
QString QQmlBinding::expression() const
{
- return QStringLiteral("function() { [code] }");
+ return QStringLiteral("function() { [native code] }");
}
void QQmlBinding::setTarget(const QQmlProperty &prop)
@@ -465,13 +564,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop)
setTarget(prop.object(), pd->core, &pd->valueTypeData);
}
-void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
+bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
{
m_target = object;
if (!object) {
m_targetIndex = QQmlPropertyIndex();
- return;
+ return false;
}
int coreIndex = core.coreIndex();
@@ -481,18 +580,19 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
int aValueTypeIndex;
if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
- m_target = 0;
+ // can't resolve id (yet)
+ m_target = nullptr;
m_targetIndex = QQmlPropertyIndex();
- return;
+ return false;
}
if (valueTypeIndex == -1)
valueTypeIndex = aValueTypeIndex;
QQmlData *data = QQmlData::get(object, false);
if (!data || !data->propertyCache) {
- m_target = 0;
+ m_target = nullptr;
m_targetIndex = QQmlPropertyIndex();
- return;
+ return false;
}
QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
Q_ASSERT(propertyData);
@@ -508,6 +608,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject());
data->propertyCache->addref();
}
+
+ return true;
}
void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
@@ -515,7 +617,12 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD
Q_ASSERT(propertyData);
QQmlData *data = QQmlData::get(*m_target, false);
- Q_ASSERT(data && data->propertyCache);
+ Q_ASSERT(data);
+
+ if (Q_UNLIKELY(!data->propertyCache)) {
+ data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject());
+ data->propertyCache->addref();
+ }
*propertyData = data->propertyCache->property(m_targetIndex.coreIndex());
Q_ASSERT(*propertyData);
@@ -530,6 +637,42 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD
}
}
+QVector<QQmlProperty> QQmlBinding::dependencies() const
+{
+ QVector<QQmlProperty> dependencies;
+ if (!m_target.data())
+ return dependencies;
+
+ for (const auto &guardList : { permanentGuards, activeGuards }) {
+ for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) {
+ if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
+ continue;
+
+ QObject *senderObject = guard->senderAsObject();
+ if (!senderObject)
+ continue;
+
+ const QMetaObject *senderMeta = senderObject->metaObject();
+ if (!senderMeta)
+ continue;
+
+ 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())));
+ }
+ }
+ }
+ }
+
+ return dependencies;
+}
+
+bool QQmlBinding::hasDependencies() const
+{
+ return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
+}
+
class QObjectPointerBinding: public QQmlNonbindingBinding
{
QQmlMetaObject targetMetaObject;
@@ -541,7 +684,7 @@ public:
protected:
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
- QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL
+ QQmlPropertyData::WriteFlags flags) override final
{
QQmlPropertyData *pd;
QQmlPropertyData vtpd;