diff options
Diffstat (limited to 'src/qml/qml/qqmlboundsignal.cpp')
-rw-r--r-- | src/qml/qml/qqmlboundsignal.cpp | 318 |
1 files changed, 120 insertions, 198 deletions
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 766e657c59..477a517e32 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -43,11 +43,12 @@ #include "qqmlcontext.h" #include "qqmlglobal_p.h" #include <private/qqmlprofiler_p.h> -#include <private/qv4debugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlcompiler_p.h> #include "qqmlinfo.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <QtCore/qstringbuilder.h> #include <QtCore/qdebug.h> @@ -55,71 +56,79 @@ QT_BEGIN_NAMESPACE -static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = { - QQmlBoundSignalExpression::expressionIdentifier, - QQmlBoundSignalExpression::expressionChanged -}; - -QQmlBoundSignalExpression::ExtraData::ExtraData(const QString &handlerName, const QString ¶meterString, - const QString &expression, const QString &fileName, - quint16 line, quint16 column) - : m_handlerName(handlerName), - m_parameterString(parameterString), - m_expression(expression), - m_sourceLocation(fileName, line, column) -{ -} - 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) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(new ExtraData(handlerName, parameterString, expression, fileName, line, column)) + m_target(target) { - setExpressionFunctionValid(false); - setInvalidParameterName(false); - init(ctxt, scope); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); + QV4::ExecutionEngine *v4 = ep->v4engine(); + + QString function; + + // Add some leading whitespace to account for the binding's column offset. + // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. + function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2); + function += QStringLiteral("(function "); + function += handlerName; + function += QLatin1Char('('); + + if (parameterString.isEmpty()) { + QString error; + //TODO: look at using the property cache here (as in the compiler) + // for further optimization + QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index); + function += QQmlPropertyCache::signalParameterStringForJS(v4, signal.parameterNames(), &error); + + if (!error.isEmpty()) { + qmlInfo(scopeObject()) << error; + return; + } + } else + function += parameterString; + + function += QStringLiteral(") { "); + function += expression; + function += QStringLiteral(" })"); + + m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line)); + + if (m_function.isNullOrUndefined()) + return; // could not evaluate function. Not valid. + } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QV4::Value &function) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_function(function.asObject()->engine(), function), - m_target(target), - m_extra(0) + m_target(target) { - setExpressionFunctionValid(true); - setInvalidParameterName(false); - + m_function.set(function.as<QV4::Object>()->engine(), function); init(ctxt, scope); } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(0) + m_target(target) { - setExpressionFunctionValid(true); - setInvalidParameterName(false); - // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index); QString error; QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); - m_function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error)); + m_function.set(engine, QV4::FunctionObject::createQmlFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error)); if (!error.isEmpty()) { qmlInfo(scopeObject()) << error; - setInvalidParameterName(true); - } else - setInvalidParameterName(false); + m_function.clear(); + } } void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) @@ -134,34 +143,30 @@ void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) QQmlBoundSignalExpression::~QQmlBoundSignalExpression() { - delete m_extra.data(); } -QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlBoundSignalExpression::expressionIdentifier() { - QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e); - QQmlSourceLocation loc = This->sourceLocation(); + QQmlSourceLocation loc = sourceLocation(); return loc.sourceFile + QLatin1Char(':') + QString::number(loc.line); } -void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *) +void QQmlBoundSignalExpression::expressionChanged() { // bound signals do not notify on change. } QQmlSourceLocation QQmlBoundSignalExpression::sourceLocation() const { - if (expressionFunctionValid()) { - QV4::Function *f = function(); - Q_ASSERT(f); + QV4::Function *f = function(); + if (f) { QQmlSourceLocation loc; loc.sourceFile = f->sourceFile(); loc.line = f->compiledFunction->location.line; loc.column = f->compiledFunction->location.column; return loc; } - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_sourceLocation; + return QQmlSourceLocation(); } QString QQmlBoundSignalExpression::expression() const @@ -171,10 +176,8 @@ QString QQmlBoundSignalExpression::expression() const QV4::Scope scope(QQmlEnginePrivate::get(engine())->v4engine()); QV4::ScopedValue v(scope, m_function.value()); return v->toQStringNoThrow(); - } else { - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_expression; } + return QString(); } QV4::Function *QQmlBoundSignalExpression::function() const @@ -194,108 +197,79 @@ void QQmlBoundSignalExpression::evaluate(void **a) { Q_ASSERT (context() && engine()); - if (invalidParameterName()) + if (!expressionFunctionValid()) return; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); QV4::Scope scope(ep->v4engine()); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - { - if (!expressionFunctionValid()) { - Q_ASSERT(!m_extra.isNull()); - QString expression; - - // Add some leading whitespace to account for the binding's column offset. - // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. - expression.fill(QChar(QChar::Space), qMax(m_extra->m_sourceLocation.column, (quint16)2) - 2); - expression += QStringLiteral("(function "); - expression += m_extra->m_handlerName; - expression += QLatin1Char('('); - - if (m_extra->m_parameterString.isEmpty()) { - QString error; - //TODO: look at using the property cache here (as in the compiler) - // for further optimization - QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index); - expression += QQmlPropertyCache::signalParameterStringForJS(scope.engine, signal.parameterNames(), &error); - - if (!error.isEmpty()) { - qmlInfo(scopeObject()) << error; - setInvalidParameterName(true); - ep->dereferenceScarceResources(); - return; - } - } else - expression += m_extra->m_parameterString; - - expression += QStringLiteral(") { "); - expression += m_extra->m_expression; - expression += QStringLiteral(" })"); - - m_extra->m_expression.clear(); - m_extra->m_handlerName.clear(); - m_extra->m_parameterString.clear(); - - m_function.set(scope.engine, evalFunction(context(), scopeObject(), expression, - m_extra->m_sourceLocation.sourceFile, m_extra->m_sourceLocation.line, &m_extra->m_v8qmlscope)); - - if (m_function.isNullOrUndefined()) { - ep->dereferenceScarceResources(); - return; // could not evaluate function. Not valid. - } - - setExpressionFunctionValid(true); - } - QVarLengthArray<int, 9> dummy; - //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, dummy, 0); - int argCount = argsTypes ? *argsTypes : 0; - - QV4::ScopedValue f(scope, m_function.value()); - QV4::ScopedCallData callData(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 == QMetaType::QVariant) { - callData->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 - callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); - } else if (ep->isQObject(type)) { - if (!*reinterpret_cast<void* const *>(a[ii + 1])) - callData->args[ii] = QV4::Primitive::nullValue(); - else - callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); - } else { - callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); - } + QVarLengthArray<int, 9> dummy; + //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, dummy, 0); + int argCount = argsTypes ? *argsTypes : 0; + + QV4::ScopedCallData callData(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 == QMetaType::QVariant) { + callData->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 + callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); + } else if (type == qMetaTypeId<QQmlV4Handle>()) { + callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); + } else if (ep->isQObject(type)) { + if (!*reinterpret_cast<void* const *>(a[ii + 1])) + callData->args[ii] = QV4::Primitive::nullValue(); + else + callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); + } else { + callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); } - - QQmlJavaScriptExpression::evaluate(context(), f, callData, 0); } + + QQmlJavaScriptExpression::evaluate(callData, 0); + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } //////////////////////////////////////////////////////////////////////// -QQmlAbstractBoundSignal::QQmlAbstractBoundSignal() -: m_prevSignal(0), m_nextSignal(0) + +/*! \internal + \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). + This is different from QMetaMethod::methodIndex(). +*/ +QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, + QQmlEngine *engine) + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlBoundSignal), + m_prevSignal(0), m_nextSignal(0), + m_expression(0) { + addToObject(owner); + + /* + If this is a cloned method, connect to the 'original'. For example, + for the signal 'void aSignal(int parameter = 0)', if the method + index refers to 'aSignal()', get the index of 'aSignal(int)'. + This ensures that 'parameter' will be available from QML. + */ + signal = QQmlPropertyCache::originalClone(target, signal); + QQmlNotifierEndpoint::connect(target, signal, engine); } -QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal() +QQmlBoundSignal::~QQmlBoundSignal() { removeFromObject(); } -void QQmlAbstractBoundSignal::addToObject(QObject *obj) +void QQmlBoundSignal::addToObject(QObject *obj) { Q_ASSERT(!m_prevSignal); Q_ASSERT(obj); @@ -308,7 +282,7 @@ void QQmlAbstractBoundSignal::addToObject(QObject *obj) data->signalHandlers = this; } -void QQmlAbstractBoundSignal::removeFromObject() +void QQmlBoundSignal::removeFromObject() { if (m_prevSignal) { *m_prevSignal = m_nextSignal; @@ -318,40 +292,6 @@ void QQmlAbstractBoundSignal::removeFromObject() } } -/*! \internal - \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, - QQmlEngine *engine) -: m_expression(0), m_index(signal), m_isEvaluating(false) -{ - addToObject(owner); - setCallback(QQmlNotifierEndpoint::QQmlBoundSignal); - - /* - If this is a cloned method, connect to the 'original'. For example, - for the signal 'void aSignal(int parameter = 0)', if the method - index refers to 'aSignal()', get the index of 'aSignal(int)'. - This ensures that 'parameter' will be available from QML. - */ - m_index = QQmlPropertyCache::originalClone(target, m_index); - QQmlNotifierEndpoint::connect(target, m_index, engine); -} - -QQmlBoundSignal::~QQmlBoundSignal() -{ - m_expression = 0; -} - -/*! - Returns the signal index in the range returned by QObjectPrivate::signalIndex(). - This is different from QMetaMethod::methodIndex(). -*/ -int QQmlBoundSignal::index() const -{ - return m_index; -} /*! Returns the signal expression. @@ -362,45 +302,29 @@ QQmlBoundSignalExpression *QQmlBoundSignal::expression() const } /*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. + Sets the signal expression to \a e. - The QQmlBoundSignal instance adds a reference to \a e. The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. + The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). */ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e) +void QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) { - QQmlBoundSignalExpressionPointer rv = m_expression; - m_expression = e; - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; -} - -/*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. - - The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. -*/ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) -{ - QQmlBoundSignalExpressionPointer rv = m_expression; m_expression.take(e); - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; + if (m_expression) + m_expression->setNotifyOnValueChanged(false); } void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) { QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e); + if (!s->m_expression) return; - if (QQmlDebugService::isDebuggingEnabled()) - QV4DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_expression->target()->metaObject(), s->m_index).methodSignature())); - - s->m_isEvaluating = true; + QV4DebugService *service = QQmlDebugConnector::service<QV4DebugService>(); + if (service) + service->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal( + s->m_expression->target()->metaObject(), + s->signalIndex()).methodSignature())); QQmlEngine *engine; if (s->m_expression && (engine = s->m_expression->engine())) { @@ -410,8 +334,6 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) QQmlEnginePrivate::warning(engine, s->m_expression->error(engine)); } } - - s->m_isEvaluating = false; } //////////////////////////////////////////////////////////////////////// |