aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmlbinding.cpp119
-rw-r--r--src/qml/qml/qqmlbinding_p.h5
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp147
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h4
4 files changed, 213 insertions, 62 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index b21468951d..b9d783736c 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -247,13 +247,28 @@ protected:
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);
+ auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
+ const QV4::Function *v4Function = function();
+ if (v4Function && v4Function->aotFunction && !hasBoundFunction()) {
+ const auto returnType = v4Function->aotFunction->returnType;
+ const auto size = returnType.sizeOf();
+ if (Q_LIKELY(size > 0)) {
+ Q_ALLOCA_VAR(void, result, size);
+ returnType.construct(result);
+ const bool isUndefined = !evaluate(result, returnType);
+ if (canWrite())
+ error = !write(result, returnType, isUndefined, flags);
+ 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()) {
@@ -273,6 +288,7 @@ protected:
}
virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
+ virtual bool write(void *result, QMetaType type, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
};
template<int StaticPropType>
@@ -280,6 +296,30 @@ class GenericBinding: public QQmlNonbindingBinding
{
protected:
// 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
+ {
+ QQmlPropertyData *pd;
+ QQmlPropertyData vpd;
+ getPropertyData(&pd, &vpd);
+ Q_ASSERT(pd);
+
+ if (Q_UNLIKELY(isUndefined || vpd.isValid())) {
+ return slowWrite(*pd, vpd, engine()->handle()->metaTypeToJS(type, result),
+ isUndefined, flags);
+ }
+
+ if ((StaticPropType == QMetaType::UnknownType && pd->propType() == type)
+ || StaticPropType == type.id()) {
+ Q_ASSERT(targetObject());
+ return pd->writeProperty(targetObject(), result, flags);
+ }
+
+ // 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);
+ }
+
+ // 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
{
@@ -684,6 +724,48 @@ public:
{}
protected:
+ Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) override final
+ {
+ 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>()) {
+ QVariant value = *static_cast<QVariant *>(result);
+ QQmlEngine *qmlEngine = engine();
+ Q_ASSERT(qmlEngine);
+ resultMo = QQmlPropertyPrivate::rawMetaObjectForType(
+ QQmlEnginePrivate::get(qmlEngine), value.userType());
+ 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
{
@@ -711,8 +793,9 @@ protected:
} else if (auto variant = result.as<QV4::VariantObject>()) {
QVariant value = variant->d()->data();
QQmlEngine *qmlEngine = engine();
+ Q_ASSERT(qmlEngine);
resultMo = QQmlPropertyPrivate::rawMetaObjectForType(
- qmlEngine ? QQmlEnginePrivate::get(qmlEngine) : nullptr, value.userType());
+ QQmlEnginePrivate::get(qmlEngine), value.userType());
if (resultMo.isNull())
return slowWrite(*pd, vtpd, result, isUndefined, flags);
resultObject = *static_cast<QObject *const *>(value.constData());
@@ -720,7 +803,25 @@ 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;
+ bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
+ void *result, QMetaType type, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags)
+ {
+ return slowWrite(core, valueTypeData, engine()->handle()->metaTypeToJS(type, result),
+ isUndefined, flags);
+ }
+
+ template<typename SlowWrite>
+ bool compareAndSet(const QQmlMetaObject &resultMo, QObject *resultObject, 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)) {
@@ -729,7 +830,7 @@ protected:
// the property type.
return pd->writeProperty(targetObject(), &resultObject, flags);
} else {
- return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ return slowWrite();
}
}
};
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index bf73c24c9e..9542f3acca 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -116,6 +116,7 @@ public:
void setBoundFunction(QV4::BoundFunction *boundFunction) {
m_boundFunction.set(boundFunction->engine(), *boundFunction);
}
+ bool hasBoundFunction() const { return m_boundFunction.valueRef(); }
/**
* This method returns a snapshot of the currently tracked dependencies of
@@ -138,6 +139,10 @@ protected:
const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags);
QV4::ReturnedValue evaluate(bool *isUndefined);
+ bool evaluate(void *result, QMetaType type)
+ {
+ return QQmlJavaScriptExpression::evaluate(&result, &type, 0);
+ }
private:
inline bool updatingFlag() const;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 260ac79c25..c84523adfe 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -172,11 +172,6 @@ void QQmlJavaScriptExpression::setContext(const QQmlRefPointer<QQmlContextData>
context->addExpression(this);
}
-QV4::Function *QQmlJavaScriptExpression::function() const
-{
- return m_v4Function;
-}
-
void QQmlJavaScriptExpression::refresh()
{
}
@@ -190,6 +185,75 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined)
return evaluate(jsCall.callData(scope), isUndefined);
}
+class QQmlJavaScriptExpressionCapture
+{
+ Q_DISABLE_COPY_MOVE(QQmlJavaScriptExpressionCapture)
+public:
+ QQmlJavaScriptExpressionCapture(QQmlJavaScriptExpression *expression, QQmlEngine *engine)
+ : watcher(expression)
+ , capture(engine, expression, &watcher)
+ , ep(QQmlEnginePrivate::get(engine))
+ {
+ Q_ASSERT(expression->notifyOnValueChanged() || expression->activeGuards.isEmpty());
+
+ lastPropertyCapture = ep->propertyCapture;
+ ep->propertyCapture = expression->notifyOnValueChanged() ? &capture : nullptr;
+
+ if (expression->notifyOnValueChanged())
+ capture.guards.copyAndClearPrepend(expression->activeGuards);
+ }
+
+ ~QQmlJavaScriptExpressionCapture()
+ {
+ if (capture.errorString) {
+ for (int ii = 0; ii < capture.errorString->count(); ++ii)
+ qWarning("%s", qPrintable(capture.errorString->at(ii)));
+ delete capture.errorString;
+ capture.errorString = nullptr;
+ }
+
+ while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst())
+ g->Delete();
+
+ if (!watcher.wasDeleted())
+ capture.expression->setTranslationsCaptured(capture.translationCaptured);
+
+ ep->propertyCapture = lastPropertyCapture;
+ }
+
+ bool catchException(const QV4::Scope &scope) const
+ {
+ if (scope.hasException()) {
+ if (watcher.wasDeleted())
+ scope.engine->catchException(); // ignore exception
+ else
+ capture.expression->delayedError()->catchJavaScriptException(scope.engine);
+ return true;
+ }
+
+ if (!watcher.wasDeleted() && capture.expression->hasDelayedError())
+ capture.expression->delayedError()->clearError();
+ return false;
+ }
+
+private:
+ QQmlJavaScriptExpression::DeleteWatcher watcher;
+ QQmlPropertyCapture capture;
+ QQmlEnginePrivate *ep;
+ QQmlPropertyCapture *lastPropertyCapture;
+};
+
+static inline QV4::ReturnedValue thisObject(QObject *scopeObject, QV4::ExecutionEngine *v4)
+{
+ if (scopeObject) {
+ // The result of wrap() can only be null, undefined, or an object.
+ const QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject);
+ if (QV4::Value::fromReturnedValue(scope).isManaged())
+ return scope;
+ }
+ return v4->globalObject->asReturnedValue();
+}
+
QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined)
{
Q_ASSERT(m_context && m_context->engine());
@@ -201,69 +265,48 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
return QV4::Encode::undefined();
}
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine());
-
// All code that follows must check with watcher before it accesses data members
// incase we have been deleted.
- DeleteWatcher watcher(this);
+ QQmlEngine *qmlEngine = m_context->engine();
+ QQmlJavaScriptExpressionCapture capture(this, qmlEngine);
- Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
- QQmlPropertyCapture capture(m_context->engine(), this, &watcher);
-
- QQmlPropertyCapture *lastPropertyCapture = ep->propertyCapture;
- ep->propertyCapture = notifyOnValueChanged() ? &capture : nullptr;
-
-
- if (notifyOnValueChanged())
- capture.guards.copyAndClearPrepend(activeGuards);
-
- QV4::ExecutionEngine *v4 = m_context->engine()->handle();
- callData->thisObject = v4->globalObject;
- if (scopeObject()) {
- QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject());
- if (QV4::Value::fromReturnedValue(scope).isObject())
- callData->thisObject = scope;
- }
+ QV4::Scope scope(qmlEngine->handle());
+ callData->thisObject = thisObject(scopeObject(), scope.engine);
Q_ASSERT(m_qmlScope.valueRef());
- QV4::ReturnedValue res = v4Function->call(
+ QV4::ScopedValue result(scope, v4Function->call(
&(callData->thisObject.asValue<QV4::Value>()),
callData->argValues<QV4::Value>(), callData->argc(),
- static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
- QV4::Scope scope(v4);
- QV4::ScopedValue result(scope, res);
+ static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())));
- if (scope.hasException()) {
- if (watcher.wasDeleted())
- scope.engine->catchException(); // ignore exception
- else
- delayedError()->catchJavaScriptException(scope.engine);
+ if (capture.catchException(scope)) {
if (isUndefined)
*isUndefined = true;
- } else {
- if (isUndefined)
- *isUndefined = result->isUndefined();
-
- if (!watcher.wasDeleted() && hasDelayedError())
- delayedError()->clearError();
+ } else if (isUndefined) {
+ *isUndefined = result->isUndefined();
}
- if (capture.errorString) {
- for (int ii = 0; ii < capture.errorString->count(); ++ii)
- qWarning("%s", qPrintable(capture.errorString->at(ii)));
- delete capture.errorString;
- capture.errorString = nullptr;
- }
+ return result->asReturnedValue();
+}
- while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst())
- g->Delete();
+bool QQmlJavaScriptExpression::evaluate(void **a, QMetaType *types, int argc)
+{
+ Q_ASSERT(m_context && m_context->engine());
- if (!watcher.wasDeleted())
- setTranslationsCaptured(capture.translationCaptured);
+ // All code that follows must check with watcher before it accesses data members
+ // incase we have been deleted.
+ QQmlEngine *qmlEngine = m_context->engine();
+ QQmlJavaScriptExpressionCapture capture(this, qmlEngine);
- ep->propertyCapture = lastPropertyCapture;
+ QV4::Scope scope(qmlEngine->handle());
+ QV4::ScopedValue self(scope, thisObject(scopeObject(), scope.engine));
- return result->asReturnedValue();
+ Q_ASSERT(m_qmlScope.valueRef());
+ Q_ASSERT(function());
+ function()->call(self, a, types, argc,
+ static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
+
+ return !capture.catchException(scope);
}
void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 499d4d430f..6e4c0ac381 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -108,6 +108,7 @@ public:
QV4::ReturnedValue evaluate(bool *isUndefined);
QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined);
+ bool evaluate(void **a, QMetaType *types, int argc);
inline bool notifyOnValueChanged() const;
@@ -135,7 +136,7 @@ public:
*listHead = this;
}
- QV4::Function *function() const;
+ QV4::Function *function() const { return m_v4Function; }
virtual void refresh();
@@ -207,6 +208,7 @@ private:
friend class QQmlPropertyCapture;
friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
friend class QQmlTranslationBinding;
+ friend class QQmlJavaScriptExpressionCapture;
// Not refcounted as the context will clear the expressions when destructed.
QQmlContextData *m_context;