diff options
Diffstat (limited to 'src/qml/qml/qqmlboundsignal.cpp')
-rw-r--r-- | src/qml/qml/qqmlboundsignal.cpp | 248 |
1 files changed, 85 insertions, 163 deletions
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index e5b78591e0..0bac2f45a2 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -1,52 +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 "qqmlmetatype_p.h" -#include "qqml.h" -#include "qqmlcontext.h" #include "qqmlglobal_p.h" #include <private/qqmlprofiler_p.h> #include <private/qqmldebugconnector_p.h> @@ -57,17 +16,21 @@ #include <private/qv4value_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4qmlcontext_p.h> #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 ¶meterString) +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 ¶meterString) : 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,81 +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 (type == qMetaTypeId<QQmlV4Handle>()) { - jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(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. } @@ -261,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); @@ -303,13 +256,12 @@ void QQmlBoundSignal::removeFromObject() } } - /*! Returns the signal expression. */ QQmlBoundSignalExpression *QQmlBoundSignal::expression() const { - return m_expression; + return m_expression.data(); } /*! @@ -319,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); } @@ -354,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)); @@ -364,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 |