aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlboundsignal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlboundsignal.cpp')
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp244
1 files changed, 84 insertions, 160 deletions
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index ff01e737ca..0bac2f45a2 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -1,51 +1,11 @@
-/****************************************************************************
-**
-** 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 "qqmlboundsignal_p.h"
#include <private/qmetaobject_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include "qqmlengine_p.h"
-#include "qqmlexpression_p.h"
-#include "qqmlcontext_p.h"
-#include "qqml.h"
-#include "qqmlcontext.h"
#include "qqmlglobal_p.h"
#include <private/qqmlprofiler_p.h>
#include <private/qqmldebugconnector_p.h>
@@ -60,14 +20,17 @@
#include <QtCore/qdebug.h>
+#include <qtqml_tracepoints_p.h>
QT_BEGIN_NAMESPACE
-QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
- QQmlContextData *ctxt, QObject *scope, const QString &expression,
- const QString &fileName, quint16 line, quint16 column,
- const QString &handlerName,
- const QString &parameterString)
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_entry, const QQmlEngine *engine, const QString &function,
+ const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_exit)
+
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
+ const QString &expression, const QString &fileName, quint16 line, quint16 column,
+ const QString &handlerName, const QString &parameterString)
: QQmlJavaScriptExpression(),
m_index(index),
m_target(target)
@@ -99,13 +62,14 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
function += QLatin1String(") { ") + expression + QLatin1String(" })");
QV4::Scope valueScope(v4);
- QV4::ScopedFunctionObject f(valueScope, evalFunction(context(), scopeObject(), function, fileName, line));
+ QV4::Scoped<QV4::JavaScriptFunctionObject> f(
+ valueScope, evalFunction(context(), scopeObject(), function, fileName, line));
QV4::ScopedContext context(valueScope, f->scope());
setupFunction(context, f->function());
}
-QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scopeObject,
- QV4::Function *function, QV4::ExecutionContext *scope)
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt,
+ QObject *scopeObject, QV4::Function *function, QV4::ExecutionContext *scope)
: QQmlJavaScriptExpression(),
m_index(index),
m_target(target)
@@ -113,14 +77,9 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
// It's important to call init first, because m_index gets remapped in case of cloned signals.
init(ctxt, scopeObject);
- QV4::ExecutionEngine *engine = ctxt->engine->handle();
+ QV4::ExecutionEngine *engine = ctxt->engine()->handle();
- // If the function is marked as having a nested function, then the user wrote:
- // onSomeSignal: function() { /*....*/ }
- // So take that nested function:
- if (auto closure = function->nestedFunction()) {
- function = closure;
- } else {
+ if (!function->isClosureWrapper()) {
QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
if (!signalParameters.isEmpty()) {
QString error;
@@ -137,10 +96,32 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
QV4::Scoped<QV4::QmlContext> qmlContext(valueScope, scope);
if (!qmlContext)
qmlContext = QV4::QmlContext::create(engine->rootContext(), ctxt, scopeObject);
- setupFunction(qmlContext, function);
+ if (auto closure = function->nestedFunction()) {
+ // If the function is marked as having a nested function, then the user wrote:
+ // onSomeSignal: function() { /*....*/ }
+ // So take that nested function:
+ setupFunction(qmlContext, closure);
+ } else {
+ setupFunction(qmlContext, function);
+
+ // If it's a closure wrapper but we cannot directly access the nested function
+ // we need to run the outer function to get the nested one.
+ if (function->isClosureWrapper()) {
+ bool isUndefined = false;
+ QV4::Scoped<QV4::JavaScriptFunctionObject> result(
+ valueScope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+
+ Q_ASSERT(!isUndefined);
+ Q_ASSERT(result->function());
+ Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
+
+ QV4::Scoped<QV4::ExecutionContext> callContext(valueScope, result->scope());
+ setupFunction(callContext, result->function());
+ }
+ }
}
-void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope)
+void QQmlBoundSignalExpression::init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope)
{
setNotifyOnValueChanged(false);
setContext(ctxt);
@@ -172,79 +153,53 @@ QString QQmlBoundSignalExpression::expression() const
return QString();
}
-// Parts of this function mirror code in QQmlExpressionPrivate::value() and v8value().
+// Parts of this function mirror code in QQmlExpressionPrivate::value() and v4Value().
// Changes made here may need to be made there and vice versa.
void QQmlBoundSignalExpression::evaluate(void **a)
{
- Q_ASSERT (context() && engine());
-
if (!expressionFunctionValid())
return;
QQmlEngine *qmlengine = engine();
+
+ // If there is no engine, we have no way to evaluate anything.
+ // This can happen on destruction.
+ if (!qmlengine)
+ return;
+
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlengine);
QV4::ExecutionEngine *v4 = qmlengine->handle();
QV4::Scope scope(v4);
ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
- QQmlMetaObject::ArgTypeStorage storage;
- //TODO: lookup via signal index rather than method index as an optimization
- int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex();
- int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, nullptr);
- int argCount = argsTypes ? *argsTypes : 0;
-
- QV4::JSCallData jsCall(scope, argCount);
- for (int ii = 0; ii < argCount; ++ii) {
- int type = argsTypes[ii + 1];
- //### ideally we would use metaTypeToJS, however it currently gives different results
- // for several cases (such as QVariant type and QObject-derived types)
- //args[ii] = engine->metaTypeToJS(type, a[ii + 1]);
- if (type == qMetaTypeId<QJSValue>()) {
- if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &jsCall->args[ii]))
- jsCall->args[ii] = *v4Value;
- else
- jsCall->args[ii] = QV4::Encode::undefined();
- } else if (type == QMetaType::QVariant) {
- jsCall->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1]));
- } else if (type == QMetaType::Int) {
- //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
- jsCall->args[ii] = QV4::Value::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
- } else if (ep->isQObject(type)) {
- if (!*reinterpret_cast<void* const *>(a[ii + 1]))
- jsCall->args[ii] = QV4::Value::nullValue();
+ if (a) {
+ //TODO: lookup via signal index rather than method index as an optimization
+ const QMetaObject *targetMeta = m_target->metaObject();
+ const QMetaMethod metaMethod = targetMeta->method(
+ QMetaObjectPrivate::signal(targetMeta, m_index).methodIndex());
+
+ int argCount = metaMethod.parameterCount();
+ QQmlMetaObject::ArgTypeStorage<9> storage;
+ storage.reserve(argCount + 1);
+ storage.append(QMetaType()); // We're not interested in the return value
+ for (int i = 0; i < argCount; ++i) {
+ const QMetaType type = metaMethod.parameterMetaType(i);
+ if (!type.isValid())
+ argCount = 0;
+ else if (type.flags().testFlag(QMetaType::IsEnumeration))
+ storage.append(type.underlyingType());
else
- jsCall->args[ii] = QV4::QObjectWrapper::wrap(v4, *reinterpret_cast<QObject* const *>(a[ii + 1]));
- } else {
- jsCall->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1]));
+ storage.append(type);
}
- }
-
- QQmlJavaScriptExpression::evaluate(jsCall.callData(), nullptr);
-
- ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
-}
-
-void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args)
-{
- Q_ASSERT (context() && engine());
-
- if (!expressionFunctionValid())
- return;
- QQmlEngine *qmlengine = engine();
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlengine);
- QV4::Scope scope(qmlengine->handle());
-
- ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
-
- QV4::JSCallData jsCall(scope, args.count());
- for (int ii = 0; ii < args.count(); ++ii) {
- jsCall->args[ii] = scope.engine->fromVariant(args[ii]);
+ QQmlJavaScriptExpression::evaluate(a, storage.constData(), argCount);
+ } else {
+ void *ignoredResult = nullptr;
+ QMetaType invalidType;
+ QQmlJavaScriptExpression::evaluate(&ignoredResult, &invalidType, 0);
}
- QQmlJavaScriptExpression::evaluate(jsCall.callData(), nullptr);
-
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
@@ -259,7 +214,7 @@ QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner,
QQmlEngine *engine)
: QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlBoundSignal),
m_prevSignal(nullptr), m_nextSignal(nullptr),
- m_enabled(true), m_expression(nullptr)
+ m_enabled(true)
{
addToObject(owner);
@@ -301,13 +256,12 @@ void QQmlBoundSignal::removeFromObject()
}
}
-
/*!
Returns the signal expression.
*/
QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
{
- return m_expression;
+ return m_expression.data();
}
/*!
@@ -317,7 +271,7 @@ QQmlBoundSignalExpression *QQmlBoundSignal::expression() const
*/
void QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e)
{
- m_expression.take(e);
+ m_expression.adopt(e);
if (m_expression)
m_expression->setNotifyOnValueChanged(false);
}
@@ -352,7 +306,12 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
QQmlEngine *engine;
if (s->m_expression && (engine = s->m_expression->engine())) {
- QQmlHandlingSignalProfiler prof(QQmlEnginePrivate::get(engine)->profiler, s->m_expression);
+ Q_TRACE_SCOPE(QQmlHandlingSignal, engine,
+ s->m_expression->function() ? s->m_expression->function()->name()->toQString() : QString(),
+ s->m_expression->sourceLocation().sourceFile, s->m_expression->sourceLocation().line,
+ s->m_expression->sourceLocation().column);
+ QQmlHandlingSignalProfiler prof(QQmlEnginePrivate::get(engine)->profiler,
+ s->m_expression.data());
s->m_expression->evaluate(a);
if (s->m_expression && s->m_expression->hasError()) {
QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
@@ -362,48 +321,13 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
////////////////////////////////////////////////////////////////////////
-QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(QQmlBoundSignalExpression *o)
-: o(o)
-{
- if (o) o->addref();
-}
-
-QQmlBoundSignalExpressionPointer::QQmlBoundSignalExpressionPointer(const QQmlBoundSignalExpressionPointer &other)
-: o(other.o)
-{
- if (o) o->addref();
-}
-
-QQmlBoundSignalExpressionPointer::~QQmlBoundSignalExpressionPointer()
-{
- if (o) o->release();
-}
-
-QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(const QQmlBoundSignalExpressionPointer &other)
-{
- if (other.o) other.o->addref();
- if (o) o->release();
- o = other.o;
- return *this;
-}
-
-QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::operator=(QQmlBoundSignalExpression *other)
-{
- if (other) other->addref();
- if (o) o->release();
- o = other;
- return *this;
-}
-
-/*!
-Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership
-of the callers reference of other.
-*/
-QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBoundSignalExpression *other)
+QQmlPropertyObserver::QQmlPropertyObserver(QQmlBoundSignalExpression *expr)
+ : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
+ auto This = static_cast<QQmlPropertyObserver*>(self);
+ This->expression->evaluate(nullptr);
+ })
{
- if (o) o->release();
- o = other;
- return *this;
+ expression.adopt(expr);
}
QT_END_NAMESPACE