aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brasser <michael.brasser@nokia.com>2012-05-21 09:27:43 +1000
committerQt by Nokia <qt-info@nokia.com>2012-08-24 00:55:55 +0200
commit0853343c33e394f35c31c161b019b2aed17f9256 (patch)
tree3f792f58979ae75f8e75a0c0ef6e7f89265b1c16 /src
parent9ee6bb0e14d968647350683eafbe80eed7a27058 (diff)
Avoid dynamic lookup of signal handler arguments
Rewrite signal handlers to include the parameters in the rewrite. Also check whether parameters are actually used when possible, and if not don't provide them to the expression. Change-Id: I7d65c05f4639979dd61035cf7478119ef7647c25 Reviewed-by: Matthew Vogt <matthew.vogt@nokia.com> Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/qml/debugger/qqmlenginedebugservice.cpp3
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp328
-rw-r--r--src/qml/qml/qqmlboundsignal_p.h41
-rw-r--r--src/qml/qml/qqmlcompiler.cpp20
-rw-r--r--src/qml/qml/qqmlinstruction_p.h1
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp11
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h3
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp142
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h8
-rw-r--r--src/qml/qml/qqmlrewrite.cpp111
-rw-r--r--src/qml/qml/qqmlrewrite_p.h43
-rw-r--r--src/qml/qml/qqmlvme.cpp5
-rw-r--r--src/qml/qml/v8/qv8contextwrapper.cpp27
-rw-r--r--src/qml/qml/v8/qv8contextwrapper_p.h7
-rw-r--r--src/quick/util/qquickconnections.cpp7
-rw-r--r--src/quick/util/qquickpropertychanges.cpp4
16 files changed, 472 insertions, 289 deletions
diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/qml/debugger/qqmlenginedebugservice.cpp
index 4f728296d3..07929121d5 100644
--- a/src/qml/debugger/qqmlenginedebugservice.cpp
+++ b/src/qml/debugger/qqmlenginedebugservice.cpp
@@ -621,7 +621,8 @@ bool QQmlEngineDebugService::setBinding(int objectId,
if (isLiteralValue) {
property.write(expression);
} else if (hasValidSignal(object, propertyName)) {
- QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(QQmlContextData::get(context), object, expression.toString(),
+ QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(property)->signalIndex(),
+ QQmlContextData::get(context), object, expression.toString(),
false, filename, line, column);
QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
} else if (property.isProperty()) {
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index 071a9ed033..0263e94757 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -53,11 +53,12 @@
#include "qqmlrewrite_p.h"
#include <private/qqmlprofilerservice_p.h>
#include <private/qv8debugservice_p.h>
+#include "qqmlinfo.h"
#include <QtCore/qstringbuilder.h>
#include <QtCore/qdebug.h>
-Q_DECLARE_METATYPE(QJSValue)
+Q_DECLARE_METATYPE(QQmlV8Handle)
QT_BEGIN_NAMESPACE
@@ -66,40 +67,56 @@ static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = {
QQmlBoundSignalExpression::expressionChanged
};
-QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
+ QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
bool isRewritten, const QString &fileName, quint16 line, quint16 column)
- : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
+ : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable),
+ m_fileName(fileName),
+ m_line(line),
+ m_column(column),
+ m_parameterCountForJS(-1),
+ m_target(target),
+ m_index(index),
+ m_expressionFunctionValid(false),
+ m_expressionFunctionRewritten(isRewritten),
+ m_invalidParameterName(false)
{
- setNotifyOnValueChanged(false);
- setContext(ctxt);
- setScopeObject(scope);
+ init(ctxt, scope);
if (isRewritten)
m_expressionUtf8 = expression;
else
m_expression = QString::fromUtf8(expression);
- m_expressionFunctionValid = false;
- m_expressionFunctionRewritten = isRewritten;
- m_fileName = fileName;
- m_line = line;
- m_column = column;
}
-QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
+QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
+ QQmlContextData *ctxt, QObject *scope, const QString &expression,
bool isRewritten, const QString &fileName, quint16 line, quint16 column)
- : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable)
+ : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable),
+ m_fileName(fileName),
+ m_line(line),
+ m_column(column),
+ m_parameterCountForJS(-1),
+ m_target(target),
+ m_index(index),
+ m_expressionFunctionValid(false),
+ m_expressionFunctionRewritten(isRewritten),
+ m_invalidParameterName(false)
{
- setNotifyOnValueChanged(false);
- setContext(ctxt);
- setScopeObject(scope);
+ init(ctxt, scope);
if (isRewritten)
m_expressionUtf8 = expression.toUtf8();
else
m_expression = expression;
- m_expressionFunctionValid = false;
- m_expressionFunctionRewritten = isRewritten;
- m_fileName = fileName;
- m_line = line;
- m_column = column;
+}
+
+void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope)
+{
+ setNotifyOnValueChanged(false);
+ setContext(ctxt);
+ setScopeObject(scope);
+
+ Q_ASSERT(m_target && m_index > -1);
+ m_index = QQmlPropertyCache::originalClone(m_target, m_index);
}
QQmlBoundSignalExpression::~QQmlBoundSignalExpression()
@@ -133,11 +150,15 @@ QString QQmlBoundSignalExpression::expression() const
}
}
-// This mirrors code in QQmlExpressionPrivate::value() and v8value().
-// Any change made here should be made there and vice versa.
-void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
+// Parts of this function mirror code in QQmlExpressionPrivate::value() and v8value().
+// Changes made here may need to be made there and vice versa.
+void QQmlBoundSignalExpression::evaluate(void **a)
{
Q_ASSERT (context() && engine());
+
+ if (m_invalidParameterName)
+ return;
+
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
@@ -146,19 +167,50 @@ void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
v8::Context::Scope context_scope(ep->v8engine()->context());
if (!m_expressionFunctionValid) {
+ //TODO: look at using the property cache here (as in the compiler)
+ // for further optimization
+ QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
+ QQmlRewrite::RewriteSignalHandler rewriter;
+
+ QString expression;
+ bool ok = true;
+
if (m_expressionFunctionRewritten) {
- m_v8function = evalFunction(context(), scopeObject(), QString::fromUtf8(m_expressionUtf8),
- m_fileName, m_line, &m_v8qmlscope);
+ expression = QString::fromUtf8(m_expressionUtf8);
+
+ //if we need parameters, and the rewrite doesn't include them,
+ //create and insert the parameter string now
+ if (m_parameterCountForJS == -1 && signal.parameterCount()) {
+ const QString &parameters = rewriter.createParameterString(signal.parameterNames(),
+ ep->v8engine()->illegalNames());
+ int index = expression.indexOf(QLatin1Char('('), 1);
+ Q_ASSERT(index > -1);
+ expression.insert(index + 1, parameters);
+
+ setParameterCountForJS(rewriter.parameterCountForJS());
+ }
+
m_expressionUtf8.clear();
} else {
- bool ok = true;
- QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
- const QString &code = rewriteSignalHandler(m_expression, QString()/*no name hint available*/, &ok);
- if (ok)
- m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope);
+ //expression is still in its original form, so perform a full rewrite
+ expression = rewriter(m_expression, QString()/*no name hint available*/, &ok,
+ signal.parameterNames(),
+ ep->v8engine()->illegalNames());
m_expression.clear();
}
+ if (rewriter.hasParameterError()) {
+ qmlInfo(scopeObject()) << rewriter.parameterError();
+ m_invalidParameterName = true;
+ ep->dereferenceScarceResources();
+ return;
+ }
+
+ if (ok) {
+ m_v8function = evalFunction(context(), scopeObject(), expression,
+ m_fileName, m_line, &m_v8qmlscope);
+ }
+
if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
ep->dereferenceScarceResources();
return; // could not evaluate function. Not valid.
@@ -168,13 +220,41 @@ void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
m_expressionFunctionValid = true;
}
- if (secondaryScope) {
- QObject *restoreSecondaryScope = 0;
- restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, secondaryScope);
+ if (!hasParameterInfo()) {
QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
- ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, restoreSecondaryScope);
} else {
- QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
+ QV8Engine *engine = ep->v8engine();
+ 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 = QQmlPropertyCache::methodParameterTypes(m_target, methodIndex, dummy, 0);
+ int argCount = argsTypes ? m_parameterCountForJS : 0;
+
+ QVarLengthArray<v8::Handle<v8::Value>, 9> args(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) {
+ args[ii] = engine->fromVariant(*((QVariant *)a[ii + 1]));
+ } else if (type == QMetaType::Int) {
+ //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
+ args[ii] = v8::Integer::New(*reinterpret_cast<const int*>(a[ii + 1]));
+ } else if (type == qMetaTypeId<QQmlV8Handle>()) {
+ args[ii] = reinterpret_cast<QQmlV8Handle *>(a[ii + 1])->toHandle();
+ } else if (ep->isQObject(type)) {
+ if (!*reinterpret_cast<void* const *>(a[ii + 1]))
+ args[ii] = v8::Null();
+ else
+ args[ii] = engine->newQObject(*reinterpret_cast<QObject* const *>(a[ii + 1]));
+ } else {
+ args[ii] = engine->fromVariant(QVariant(type, a[ii + 1]));
+ }
+ }
+
+ QQmlJavaScriptExpression::evaluate(context(), m_v8function, argCount, args.data(), 0);
}
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
@@ -182,34 +262,6 @@ void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope)
////////////////////////////////////////////////////////////////////////
-class QQmlBoundSignalParameters : public QObject
-{
-Q_OBJECT
-public:
- QQmlBoundSignalParameters(const QMetaMethod &, QQmlAbstractBoundSignal*, QQmlEngine*);
- ~QQmlBoundSignalParameters();
-
- void setValues(void **);
- void clearValues();
-
-private:
- friend class MetaObject;
- int metaCall(QMetaObject::Call, int _id, void **);
- struct MetaObject : public QAbstractDynamicMetaObject {
- MetaObject(QQmlBoundSignalParameters *b)
- : parent(b) {}
-
- int metaCall(QMetaObject::Call c, int id, void **a) {
- return parent->metaCall(c, id, a);
- }
- QQmlBoundSignalParameters *parent;
- };
-
- int *types;
- void **values;
- QMetaObject *myMetaObject;
-};
-
QQmlAbstractBoundSignal::QQmlAbstractBoundSignal()
: m_prevSignal(0), m_nextSignal(0)
{
@@ -247,12 +299,10 @@ void QQmlAbstractBoundSignal::removeFromObject()
\a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
This is different from QMetaMethod::methodIndex().
*/
-QQmlBoundSignal::QQmlBoundSignal(QObject *scope, int signal, QObject *owner,
+QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner,
QQmlEngine *engine)
-: m_expression(0), m_params(0), m_scope(scope), m_index(signal)
+: m_expression(0), m_index(signal), m_isEvaluating(false)
{
- setParamsValid(false);
- setIsEvaluating(false);
addToObject(owner);
setCallback(QQmlNotifierEndpoint::QQmlBoundSignal);
@@ -262,22 +312,13 @@ QQmlBoundSignal::QQmlBoundSignal(QObject *scope, int signal, QObject *owner,
index refers to 'aSignal()', get the index of 'aSignal(int)'.
This ensures that 'parameter' will be available from QML.
*/
- if (QQmlData::get(scope, false) && QQmlData::get(scope, false)->propertyCache) {
- QQmlPropertyCache *cache = QQmlData::get(scope, false)->propertyCache;
- while (cache->signal(m_index)->isCloned())
- --m_index;
- } else {
- while (QMetaObjectPrivate::signal(scope->metaObject(), m_index).attributes() & QMetaMethod::Cloned)
- --m_index;
- }
-
- QQmlNotifierEndpoint::connect(scope, m_index, engine);
+ m_index = QQmlPropertyCache::originalClone(target, m_index);
+ QQmlNotifierEndpoint::connect(target, m_index, engine);
}
QQmlBoundSignal::~QQmlBoundSignal()
{
m_expression = 0;
- delete m_params;
}
/*!
@@ -334,140 +375,21 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
return;
if (QQmlDebugService::isDebuggingEnabled())
- QV8DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_scope->metaObject(), s->m_index).methodSignature()));
+ QV8DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_expression->target()->metaObject(), s->m_index).methodSignature()));
QQmlHandlingSignalProfiler prof(s->m_expression);
- s->setIsEvaluating(true);
-
- if (!s->paramsValid()) {
- QList<QByteArray> names = QQmlPropertyCache::signalParameterNames(*s->m_scope, s->m_index);
- if (!names.isEmpty()) {
- QMetaMethod signal = QMetaObjectPrivate::signal(s->m_scope->metaObject(), s->m_index);
- s->m_params = new QQmlBoundSignalParameters(signal, s, s->m_expression->engine());
- }
+ s->m_isEvaluating = true;
- s->setParamsValid(true);
- }
-
- if (s->m_params) s->m_params->setValues(a);
if (s->m_expression && s->m_expression->engine()) {
- s->m_expression->evaluate(s->m_params);
+ s->m_expression->evaluate(a);
if (s->m_expression && s->m_expression->hasError()) {
QQmlEngine *engine = s->m_expression->engine();
QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
}
}
- if (s->m_params) s->m_params->clearValues();
-
- s->setIsEvaluating(false);
-}
-
-QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method,
- QQmlAbstractBoundSignal *owner,
- QQmlEngine *engine)
-: types(0), values(0)
-{
- MetaObject *mo = new MetaObject(this);
-
- // ### Optimize!
- QMetaObjectBuilder mob;
- mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
- mob.setClassName("QQmlBoundSignalParameters");
-
- QList<QByteArray> paramTypes = method.parameterTypes();
- QList<QByteArray> paramNames = method.parameterNames();
- types = new int[paramTypes.count()];
- for (int ii = 0; ii < paramTypes.count(); ++ii) {
- const QByteArray &type = paramTypes.at(ii);
- if (type.isEmpty()) {
- types[ii] = 0;
- continue;
- }
-
- QByteArray name = paramNames.at(ii);
- if (name.isEmpty())
- name = "__qt_anonymous_param_" + QByteArray::number(ii);
-
- int t = QMetaType::type(type.constData());
- if (QQmlEnginePrivate::get(engine)->isQObject(t)) {
- types[ii] = QMetaType::QObjectStar;
- QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
- prop.setWritable(false);
- } else {
- QByteArray propType = type;
- QMetaType::TypeFlags flags = QMetaType::typeFlags(t);
- if (flags & QMetaType::IsEnumeration) {
- t = QVariant::Int;
- propType = "int";
- } else if (t == QMetaType::UnknownType ||
- (t >= int(QMetaType::User) && !(flags & QMetaType::PointerToQObject) &&
- t != qMetaTypeId<QJSValue>())) {
- //the UserType clause is to catch registered QFlags
- QByteArray scope;
- QByteArray name;
- int scopeIdx = propType.lastIndexOf("::");
- if (scopeIdx != -1) {
- scope = propType.left(scopeIdx);
- name = propType.mid(scopeIdx + 2);
- } else {
- name = propType;
- }
- const QMetaObject *meta;
- if (scope == "Qt")
- meta = &QObject::staticQtMetaObject;
- else
- meta = owner->scope()->metaObject();
- for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
- QMetaEnum m = meta->enumerator(i);
- if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
- t = QVariant::Int;
- propType = "int";
- break;
- }
- }
- }
- types[ii] = t;
- QMetaPropertyBuilder prop = mob.addProperty(name, propType);
- prop.setWritable(false);
- }
- }
- myMetaObject = mob.toMetaObject();
- *static_cast<QMetaObject *>(mo) = *myMetaObject;
-
- d_ptr->metaObject = mo;
-}
-
-QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
-{
- delete [] types;
- free(myMetaObject);
-}
-
-void QQmlBoundSignalParameters::setValues(void **v)
-{
- values = v;
-}
-void QQmlBoundSignalParameters::clearValues()
-{
- values = 0;
-}
-
-int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
-{
- if (!values)
- return -1;
-
- if (c == QMetaObject::ReadProperty && id >= 1) {
- int t = types[id - 1];
- void *p = a[0];
- QMetaType::destruct(t, p);
- QMetaType::construct(t, p, values[id]);
- return -1;
- } else {
- return qt_metacall(c, id, a);
- }
+ s->m_isEvaluating = false;
}
////////////////////////////////////////////////////////////////////////
@@ -517,5 +439,3 @@ QQmlBoundSignalExpressionPointer &QQmlBoundSignalExpressionPointer::take(QQmlBou
}
QT_END_NAMESPACE
-
-#include <qqmlboundsignal.moc>
diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h
index 879b84294c..cdef579b09 100644
--- a/src/qml/qml/qqmlboundsignal_p.h
+++ b/src/qml/qml/qqmlboundsignal_p.h
@@ -62,35 +62,43 @@
#include <private/qflagpointer_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmlglobal_p.h>
-#include <private/qobject_p.h>
+#include <private/qbitfield_p.h>
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlAbstractExpression, public QQmlJavaScriptExpression, public QQmlRefCount
{
public:
- QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
+ QQmlBoundSignalExpression(QObject *target, int index,
+ QQmlContextData *ctxt, QObject *scope, const QByteArray &expression,
bool isRewritten, const QString &fileName, quint16 line, quint16 column);
- QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression,
+ QQmlBoundSignalExpression(QObject *target, int index,
+ QQmlContextData *ctxt, QObject *scope, const QString &expression,
bool isRewritten, const QString &fileName, quint16 line, quint16 column);
// "inherited" from QQmlJavaScriptExpression.
static QString expressionIdentifier(QQmlJavaScriptExpression *);
static void expressionChanged(QQmlJavaScriptExpression *);
+ void setParameterCountForJS(int count) { m_parameterCountForJS = count; }
+
// evaluation of a bound signal expression doesn't return any value
- void evaluate(QObject *secondaryScope = 0);
+ void evaluate(void **a);
QString sourceFile() const { return m_fileName; }
quint16 lineNumber() const { return m_line; }
quint16 columnNumber() const { return m_column; }
QString expression() const;
+ QObject *target() const { return m_target; }
QQmlEngine *engine() const { return context() ? context()->engine : 0; }
private:
~QQmlBoundSignalExpression();
+ void init(QQmlContextData *ctxt, QObject *scope);
+ bool hasParameterInfo() const { return m_parameterCountForJS > 0; }
+
v8::Persistent<v8::Object> m_v8qmlscope;
v8::Persistent<v8::Function> m_v8function;
@@ -99,13 +107,18 @@ private:
//extract it from m_v8function if needed.
QByteArray m_expressionUtf8;
QString m_expression; //only used when expression needs to be rewritten
-
QString m_fileName;
quint16 m_line;
quint16 m_column;
+ int m_parameterCountForJS;
+
+ QObject *m_target;
+ int m_index;
+
bool m_expressionFunctionValid:1;
bool m_expressionFunctionRewritten:1;
+ bool m_invalidParameterName:1;
};
class Q_QML_PRIVATE_EXPORT QQmlAbstractBoundSignal
@@ -118,7 +131,6 @@ public:
virtual QQmlBoundSignalExpression *expression() const = 0;
virtual QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *) = 0;
virtual QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *) = 0;
- virtual QObject *scope() = 0;
virtual bool isEvaluating() const = 0;
void removeFromObject();
@@ -133,12 +145,11 @@ private:
QQmlAbstractBoundSignal *m_nextSignal;
};
-class QQmlBoundSignalParameters;
class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlAbstractBoundSignal,
public QQmlNotifierEndpoint
{
public:
- QQmlBoundSignal(QObject *scope, int signal, QObject *owner, QQmlEngine *engine);
+ QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine);
virtual ~QQmlBoundSignal();
int index() const;
@@ -146,27 +157,17 @@ public:
QQmlBoundSignalExpression *expression() const;
QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *);
QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *);
- QObject *scope() { return *m_scope; }
- bool isEvaluating() const { return m_scope.flag(); }
+ bool isEvaluating() const { return m_isEvaluating; }
private:
friend void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **);
QQmlBoundSignalExpressionPointer m_expression;
- QQmlBoundSignalParameters *m_params;
- // We store some flag bits in the following flag pointer.
- // m_scope:flag1 - m_isEvaluating
- // m_scope:flag2 - m_paramsValid
- QFlagPointer<QObject> m_scope;
int m_index;
-
- void setIsEvaluating(bool v) { m_scope.setFlagValue(v); }
- void setParamsValid(bool v) { m_scope.setFlag2Value(v); }
- bool paramsValid() const { return m_scope.flag2(); }
+ bool m_isEvaluating;
};
-
QT_END_NAMESPACE
#endif // QQMLBOUNDSIGNAL_P_H
diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp
index ec1d19dd4e..ee8b30aab7 100644
--- a/src/qml/qml/qqmlcompiler.cpp
+++ b/src/qml/qml/qqmlcompiler.cpp
@@ -1323,8 +1323,17 @@ void QQmlCompiler::genObjectBody(QQmlScript::Object *obj)
Instruction::StoreSignal store;
store.signalIndex = prop->index;
- const QString &rewrite = rewriteSignalHandler(v->value, prop->name().toString());
+
+ const QList<QByteArray> &parameterNameList = obj->metatype->signalParameterNames(prop->index);
+ QQmlRewrite::RewriteSignalHandler rewriter;
+ int count = 0;
+ const QString &rewrite = rewriter(v->value.asAST(), v->value.asScript(),
+ prop->name().toString(),
+ obj->metatype->signalParameterStringForJS(prop->index, &count),
+ parameterNameList);
store.value = output->indexForByteArray(rewrite.toUtf8());
+ store.parameterCount =
+ (rewriter.parameterAccess() == QQmlRewrite::RewriteSignalHandler::ParametersUnaccessed) ? 0 : count;
store.context = v->signalExpressionContextStack;
store.line = v->location.start.line;
store.column = v->location.start.column;
@@ -1694,6 +1703,15 @@ bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *o
if (script.isEmpty())
COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
+ //all handlers should be on the original, rather than cloned signals in order
+ //to ensure all parameters are available (see qqmlboundsignal constructor for more details)
+ prop->index = obj->metatype->originalClone(prop->index);
+
+ QString errorString;
+ obj->metatype->signalParameterStringForJS(prop->index, 0, &errorString);
+ if (!errorString.isEmpty())
+ COMPILE_EXCEPTION(prop, errorString);
+
prop->values.first()->signalExpressionContextStack = ctxt.stack;
}
}
diff --git a/src/qml/qml/qqmlinstruction_p.h b/src/qml/qml/qqmlinstruction_p.h
index db196276d5..49bbd0e54c 100644
--- a/src/qml/qml/qqmlinstruction_p.h
+++ b/src/qml/qml/qqmlinstruction_p.h
@@ -408,6 +408,7 @@ union QQmlInstruction
QML_INSTR_HEADER
int signalIndex;
int value;
+ int parameterCount;
short context;
ushort line;
ushort column;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 650123bb8c..c72ca545f4 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -124,6 +124,15 @@ v8::Local<v8::Value>
QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
v8::Handle<v8::Function> function, bool *isUndefined)
{
+ return evaluate(context, function, 0, 0, isUndefined);
+}
+
+v8::Local<v8::Value>
+QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
+ v8::Handle<v8::Function> function,
+ int argc, v8::Handle<v8::Value> args[],
+ bool *isUndefined)
+{
Q_ASSERT(context && context->engine);
if (function.IsEmpty() || function->IsUndefined()) {
@@ -168,7 +177,7 @@ QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
}
- result = function->Call(This, 0, 0);
+ result = function->Call(This, argc, args);
if (isUndefined)
*isUndefined = try_catch.HasCaught() || result->IsUndefined();
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 09da661186..75b7d7560a 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -112,6 +112,9 @@ public:
v8::Local<v8::Value> evaluate(QQmlContextData *, v8::Handle<v8::Function>,
bool *isUndefined);
+ v8::Local<v8::Value> evaluate(QQmlContextData *, v8::Handle<v8::Function>,
+ int argc, v8::Handle<v8::Value> args[],
+ bool *isUndefined);
inline bool requiresThisObject() const;
inline void setRequiresThisObject(bool v);
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 89febc24cb..3519d46017 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -48,6 +48,7 @@
#include <private/qmetaobject_p.h>
#include <private/qqmlaccessors_p.h>
#include <private/qmetaobjectbuilder_p.h>
+#include <private/qqmlrewrite_p.h>
#include <QtCore/qdebug.h>
@@ -70,6 +71,12 @@ class QQmlPropertyCacheMethodArguments
public:
QQmlPropertyCacheMethodArguments *next;
+ //for signal handler rewrites
+ QString *signalParameterStringForJS;
+ int signalParameterCountForJS:30;
+ int parameterError:1;
+ int argumentsValid:1;
+
QList<QByteArray> *names;
int arguments[0];
};
@@ -261,6 +268,7 @@ QQmlPropertyCache::~QQmlPropertyCache()
QQmlPropertyCacheMethodArguments *args = argumentsCache;
while (args) {
QQmlPropertyCacheMethodArguments *next = args->next;
+ if (args->signalParameterStringForJS) delete args->signalParameterStringForJS;
if (args->names) delete args->names;
free(args);
args = next;
@@ -392,6 +400,10 @@ void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int cor
typedef QQmlPropertyCacheMethodArguments A;
A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
+ args->argumentsValid = true;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
args->names = new QList<QByteArray>(names);
args->next = argumentsCache;
argumentsCache = args;
@@ -432,6 +444,10 @@ void QQmlPropertyCache::appendSignal(const QHashedCStringRef &name, quint32 flag
typedef QQmlPropertyCacheMethodArguments A;
A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
+ args->argumentsValid = true;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
args->names = new QList<QByteArray>(names);
args->next = argumentsCache;
argumentsCache = args;
@@ -468,6 +484,10 @@ void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int cor
args->arguments[0] = argumentCount;
for (int ii = 0; ii < argumentCount; ++ii)
args->arguments[ii + 1] = QMetaType::QVariant;
+ args->argumentsValid = true;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
args->names = 0;
if (argumentCount)
args->names = new QList<QByteArray>(names);
@@ -503,6 +523,10 @@ void QQmlPropertyCache::appendMethod(const QHashedCStringRef &name, quint32 flag
args->arguments[0] = argumentCount;
for (int ii = 0; ii < argumentCount; ++ii)
args->arguments[ii + 1] = QMetaType::QVariant;
+ args->argumentsValid = true;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
args->names = 0;
if (argumentCount)
args->names = new QList<QByteArray>(names);
@@ -1001,16 +1025,60 @@ static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type)
\a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
This is different from QMetaMethod::methodIndex().
*/
-QList<QByteArray> QQmlPropertyCache::signalParameterNames(QObject *object, int index)
+QString QQmlPropertyCache::signalParameterStringForJS(int index, int *count, QString *errorString)
{
- QQmlData *data = QQmlData::get(object, false);
- if (data->propertyCache) {
- QQmlPropertyData *p = data->propertyCache->signal(index);
- if (!p->hasArguments())
- return QList<QByteArray>();
+ QQmlPropertyData *signalData = signal(index);
+ if (!signalData)
+ return QString();
+
+ typedef QQmlPropertyCacheMethodArguments A;
+
+ if (signalData->arguments) {
+ A *arguments = static_cast<A *>(signalData->arguments);
+ if (arguments->signalParameterStringForJS) {
+ if (count)
+ *count = arguments->signalParameterCountForJS;
+ if (arguments->parameterError) {
+ if (errorString)
+ *errorString = *arguments->signalParameterStringForJS;
+ return QString();
+ }
+ return *arguments->signalParameterStringForJS;
+ }
}
- return QMetaObjectPrivate::signal(object->metaObject(), index).parameterNames();
+ QList<QByteArray> parameterNameList = signalParameterNames(index);
+
+ if (!signalData->arguments) {
+ int argc = parameterNameList.count();
+ A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
+ args->arguments[0] = argc;
+ args->argumentsValid = false;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
+ args->names = new QList<QByteArray>(parameterNameList);
+ signalData->arguments = args;
+ }
+
+ QQmlRewrite::RewriteSignalHandler rewriter;
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ const QString &parameters = rewriter.createParameterString(parameterNameList,
+ ep->v8engine()->illegalNames());
+
+ bool error = rewriter.hasParameterError();
+ A *arguments = static_cast<A *>(signalData->arguments);
+ arguments->signalParameterStringForJS = new QString(error ? rewriter.parameterError() : parameters);
+ arguments->signalParameterCountForJS = rewriter.parameterCountForJS();
+ if (count)
+ *count = arguments->signalParameterCountForJS;
+ if (error) {
+ arguments->parameterError = true;
+ if (errorString)
+ *errorString = *arguments->signalParameterStringForJS;
+ return QString();
+ }
+ return *arguments->signalParameterStringForJS;
}
// Returns an array of the arguments for method \a index. The first entry in the array
@@ -1034,7 +1102,7 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index,
QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
- if (rv->arguments)
+ if (rv->arguments && static_cast<A *>(rv->arguments)->argumentsValid)
return static_cast<A *>(rv->arguments)->arguments;
const QMetaObject *metaObject = c->createMetaObject();
@@ -1042,9 +1110,18 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index,
QMetaMethod m = metaObject->method(index);
int argc = m.parameterCount();
- A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
- args->arguments[0] = argc;
- args->names = 0;
+ if (!rv->arguments) {
+ A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
+ args->arguments[0] = argc;
+ args->argumentsValid = false;
+ args->signalParameterStringForJS = 0;
+ args->signalParameterCountForJS = 0;
+ args->parameterError = false;
+ args->names = 0;
+ rv->arguments = args;
+ }
+ A *args = static_cast<A *>(rv->arguments);
+
QList<QByteArray> argTypeNames; // Only loaded if needed
for (int ii = 0; ii < argc; ++ii) {
@@ -1062,13 +1139,12 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index,
}
if (type == QMetaType::UnknownType) {
if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
- free(args);
return 0;
}
args->arguments[ii + 1] = type;
}
+ args->argumentsValid = true;
- rv->arguments = args;
args->next = c->argumentsCache;
c->argumentsCache = args;
return static_cast<A *>(rv->arguments)->arguments;
@@ -1154,6 +1230,27 @@ int QQmlPropertyCache::methodReturnType(QObject *object, const QQmlPropertyData
return type;
}
+int QQmlPropertyCache::originalClone(int index)
+{
+ while (signal(index)->isCloned())
+ --index;
+ return index;
+}
+
+int QQmlPropertyCache::originalClone(QObject *object, int index)
+{
+ QQmlData *data = QQmlData::get(object, false);
+ if (data && data->propertyCache) {
+ QQmlPropertyCache *cache = data->propertyCache;
+ while (cache->signal(index)->isCloned())
+ --index;
+ } else {
+ while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
+ --index;
+ }
+ return index;
+}
+
QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const QString &property)
{
Q_ASSERT(metaObject);
@@ -1355,7 +1452,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
QQmlPropertyCacheMethodArguments *arguments = 0;
if (data->hasArguments()) {
arguments = (QQmlPropertyCacheMethodArguments *)data->arguments;
-
+ Q_ASSERT(arguments->argumentsValid);
for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
if (ii != 0) signature.append(",");
signature.append(QMetaType::typeName(arguments->arguments[1 + ii]));
@@ -1388,6 +1485,23 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
}
}
+/*! \internal
+ \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
+ This is different from QMetaMethod::methodIndex().
+*/
+QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
+{
+ QQmlPropertyData *signalData = signal(index);
+ if (signalData && signalData->hasArguments()) {
+ QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments;
+ if (args && args->names)
+ return *args->names;
+ const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
+ return method.parameterNames();
+ }
+ return QList<QByteArray>();
+}
+
// Returns true if \a from is assignable to a property of type \a to
bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
{
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 87d15b3d62..b14e2f4c1f 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -300,7 +300,13 @@ public:
QByteArray *unknownTypeError);
static int methodReturnType(QObject *, const QQmlPropertyData &data,
QByteArray *unknownTypeError);
- static QList<QByteArray> signalParameterNames(QObject *, int index);
+
+ //see QMetaObjectPrivate::originalClone
+ int originalClone(int index);
+ static int originalClone(QObject *, int index);
+
+ QList<QByteArray> signalParameterNames(int index) const;
+ QString signalParameterStringForJS(int index, int *count = 0, QString *errorString = 0);
const char *className() const;
diff --git a/src/qml/qml/qqmlrewrite.cpp b/src/qml/qml/qqmlrewrite.cpp
index d498f98f50..50e732bb59 100644
--- a/src/qml/qml/qqmlrewrite.cpp
+++ b/src/qml/qml/qqmlrewrite.cpp
@@ -377,10 +377,25 @@ void RewriteBinding::rewriteCaseStatements(AST::StatementList *statements, bool
}
}
+
+/*
+ RewriteSignalHandler performs two different types of rewrites, depending on what information
+ is available.
+
+ When the target object is known, the rewriter can be provided a list of parameter names (and an
+ optional preconstructed parameter string), which allows us to:
+ 1. Check whether the parameters are used
+ 2. Rewrite with the parameters included in the rewrite
+ When this information is not available, we do a more generic rewrite, and rely on the expression
+ to perform a second rewrite with the parameter information (using createParameterString)
+ once the target object is known.
+*/
RewriteSignalHandler::RewriteSignalHandler()
: _writer(0)
, _code(0)
, _position(0)
+ , _parameterAccess(UnknownAccess)
+ , _parameterCountForJS(0)
{
}
@@ -395,7 +410,75 @@ bool RewriteSignalHandler::visit(AST::StringLiteral *ast)
return false;
}
-QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name)
+//if we never make use of the signal parameters in our expression,
+//there is no need to provide them
+bool RewriteSignalHandler::visit(AST::IdentifierExpression *e)
+{
+ //optimization: don't need to compare strings if a parameter has already been marked as used.
+ if (_parameterAccess == ParametersAccessed)
+ return false;
+
+ static const QString argumentsString = QStringLiteral("arguments");
+ if (_parameterNames.contains(e->name) || e->name == argumentsString)
+ _parameterAccess = ParametersAccessed;
+ return false;
+}
+
+static QString unnamed_error_string(QLatin1String(QT_TR_NOOP("Signal uses unnamed parameter followed by named parameter.")));
+static QString global_error_string(QLatin1String(QT_TR_NOOP("Signal parameter \"%1\" hides global variable.")));
+
+#define EXIT_ON_ERROR(error) \
+{ \
+ _error = error; \
+ return QString(); \
+}
+
+//create a parameter string which can be inserted into a generic rewrite
+QString RewriteSignalHandler::createParameterString(const QList<QByteArray> &parameterNameList,
+ const QStringHash<bool> &illegalNames)
+{
+ QList<QHashedString> hashedParameterNameList;
+ for (int i = 0; i < parameterNameList.count(); ++i)
+ hashedParameterNameList.append(QString::fromUtf8(parameterNameList.at(i).constData()));
+
+ return createParameterString(hashedParameterNameList, illegalNames);
+}
+
+QString RewriteSignalHandler::createParameterString(const QList<QHashedString> &parameterNameList,
+ const QStringHash<bool> &illegalNames)
+{
+ QString parameters;
+ bool unnamedParam = false;
+ for (int i = 0; i < parameterNameList.count(); ++i) {
+ const QHashedString &param = parameterNameList.at(i);
+ if (param.isEmpty())
+ unnamedParam = true;
+ else if (unnamedParam)
+ EXIT_ON_ERROR(unnamed_error_string)
+ else if (illegalNames.contains(param))
+ EXIT_ON_ERROR(global_error_string.arg(param))
+ ++_parameterCountForJS;
+ parameters += param;
+ if (i < parameterNameList.count()-1)
+ parameters += QStringLiteral(",");
+ }
+ if (parameters.endsWith(QLatin1Char(',')))
+ parameters.resize(parameters.length() - 1);
+ return parameters;
+}
+
+/*
+ If \a parameterString is provided, use \a parameterNameList to test whether the
+ parameters are used in the body of the function
+ * if unused, the rewrite will not include parameters, else
+ * if used, the rewrite will use \a parameterString
+ If \a parameterString is not provided, it is constructed from \a parameterNameList
+ as needed.
+*/
+QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name,
+ const QString &parameterString,
+ const QList<QByteArray> &parameterNameList,
+ const QStringHash<bool> &illegalNames)
{
if (rewriteDump()) {
qWarning() << "=============================================================";
@@ -403,11 +486,26 @@ QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString
qWarning() << qPrintable(code);
}
+ bool hasParameterString = !parameterString.isEmpty();
+
QQmlJS::AST::ExpressionNode *expression = node->expressionCast();
QQmlJS::AST::Statement *statement = node->statementCast();
if (!expression && !statement)
return code;
+ if (!parameterNameList.isEmpty()) {
+ for (int i = 0; i < parameterNameList.count(); ++i) {
+ QHashedString param(QString::fromUtf8(parameterNameList.at(i).constData()));
+ _parameterNames.insert(param, i);
+ if (!hasParameterString)
+ _parameterNameList.append(param);
+ }
+
+ //this is set to Unaccessed here, and will be set to Accessed
+ //if we detect that a parameter has been used
+ _parameterAccess = ParametersUnaccessed;
+ }
+
TextWriter w;
_writer = &w;
_code = &code;
@@ -418,7 +516,10 @@ QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString
QString rewritten = code;
w.write(&rewritten);
- rewritten = QStringLiteral("(function ") + name + QStringLiteral("() { ") + rewritten + QStringLiteral(" })");
+ QString parameters = (_parameterAccess == ParametersUnaccessed) ? QString()
+ : hasParameterString ? parameterString
+ : createParameterString(_parameterNameList, illegalNames);
+ rewritten = QStringLiteral("(function ") + name + QStringLiteral("(") + parameters + QStringLiteral(") { ") + rewritten + QStringLiteral(" })");
if (rewriteDump()) {
qWarning() << "To:";
@@ -429,7 +530,9 @@ QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString
return rewritten;
}
-QString RewriteSignalHandler::operator()(const QString &code, const QString &name, bool *ok)
+QString RewriteSignalHandler::operator()(const QString &code, const QString &name, bool *ok,
+ const QList<QByteArray> &parameterNameList,
+ const QStringHash<bool> &illegalNames)
{
Engine engine;
Lexer lexer(&engine);
@@ -441,7 +544,7 @@ QString RewriteSignalHandler::operator()(const QString &code, const QString &nam
return QString();
}
if (ok) *ok = true;
- return operator()(parser.statement(), code, name);
+ return operator()(parser.statement(), code, name, QString(), parameterNameList, illegalNames);
}
} // namespace QQmlRewrite
diff --git a/src/qml/qml/qqmlrewrite_p.h b/src/qml/qml/qqmlrewrite_p.h
index 9fcc9896e6..26027a0ded 100644
--- a/src/qml/qml/qqmlrewrite_p.h
+++ b/src/qml/qml/qqmlrewrite_p.h
@@ -57,6 +57,7 @@
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsmemorypool_p.h>
+#include <private/qhashedstring_p.h>
QT_BEGIN_NAMESPACE
@@ -129,14 +130,30 @@ private:
class RewriteSignalHandler: protected AST::Visitor
{
- TextWriter *_writer;
- const QString *_code;
- int _position;
-
public:
RewriteSignalHandler();
- QString operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name);
- QString operator()(const QString &code, const QString &name, bool *ok = 0);
+ QString operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name,
+ const QString &parameterString = QString(),
+ const QList<QByteArray> &parameterNameList = QList<QByteArray>(),
+ const QStringHash<bool> &illegalNames = QStringHash<bool>());
+ QString operator()(const QString &code, const QString &name, bool *ok = 0,
+ const QList<QByteArray> &parameterNameList = QList<QByteArray>(),
+ const QStringHash<bool> &illegalNames = QStringHash<bool>());
+
+ enum ParameterAccess {
+ ParametersAccessed,
+ ParametersUnaccessed,
+ UnknownAccess
+ };
+
+ //returns the first n signal parameters that are used in the expression
+ int parameterCountForJS() const { return _parameterCountForJS; }
+ ParameterAccess parameterAccess() const { return _parameterAccess; }
+ QString createParameterString(const QList<QByteArray> &parameterNameList,
+ const QStringHash<bool> &illegalNames);
+
+ bool hasParameterError() { return !_error.isEmpty(); }
+ QString parameterError() const { return _error; }
protected:
void rewriteMultilineStrings(QString &code);
@@ -144,6 +161,20 @@ protected:
using AST::Visitor::visit;
void accept(AST::Node *node);
virtual bool visit(AST::StringLiteral *ast);
+ virtual bool visit(AST::IdentifierExpression *);
+
+private:
+ QString createParameterString(const QList<QHashedString> &parameterNameList,
+ const QStringHash<bool> &illegalNames);
+
+ TextWriter *_writer;
+ const QString *_code;
+ int _position;
+ QStringHash<int> _parameterNames;
+ QList<QHashedString> _parameterNameList;
+ ParameterAccess _parameterAccess;
+ int _parameterCountForJS;
+ QString _error;
};
bool SharedBindingTester::visit(AST::FunctionDeclaration *)
diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp
index e565321eb9..65658243ea 100644
--- a/src/qml/qml/qqmlvme.cpp
+++ b/src/qml/qml/qqmlvme.cpp
@@ -760,7 +760,10 @@ QObject *QQmlVME::run(QList<QQmlError> *errors,
QQmlBoundSignal *bs = new QQmlBoundSignal(target, instr.signalIndex, target, engine);
QQmlBoundSignalExpression *expr =
- new QQmlBoundSignalExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column);
+ new QQmlBoundSignalExpression(target, instr.signalIndex,
+ CTXT, context, DATAS.at(instr.value),
+ true, COMP->name, instr.line, instr.column);
+ expr->setParameterCountForJS(instr.parameterCount);
bs->takeExpression(expr);
QML_END_INSTR(StoreSignal)
diff --git a/src/qml/qml/v8/qv8contextwrapper.cpp b/src/qml/qml/v8/qv8contextwrapper.cpp
index 435ac69ce3..82ff64fac5 100644
--- a/src/qml/qml/v8/qv8contextwrapper.cpp
+++ b/src/qml/qml/v8/qv8contextwrapper.cpp
@@ -66,8 +66,6 @@ public:
quint32 readOnly:1;
quint32 dummy:29;
- QObject *secondaryScope;
-
// This is a pretty horrible hack, and an abuse of external strings. When we create a
// sub-context (a context created by a Qt.include() in an external javascript file),
// we pass a specially crafted SubContext external string as the v8::Script::Data() to
@@ -90,7 +88,7 @@ private:
QV8ContextResource::QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject)
: QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true),
- secondaryScope(0), context(context), scopeObject(scopeObject)
+ context(context), scopeObject(scopeObject)
{
}
@@ -209,16 +207,6 @@ void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Hand
script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt)));
}
-QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope)
-{
- QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt);
- if (!resource) return 0;
-
- QObject *rv = resource->secondaryScope;
- resource->secondaryScope = scope;
- return rv;
-}
-
QQmlContextData *QV8ContextWrapper::callingContext()
{
v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
@@ -262,7 +250,6 @@ v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
return v8::Handle<v8::Value>();
// Search type (attached property/enum/imported scripts) names
- // Secondary scope object
// while (context) {
// Search context properties
// Search scope object
@@ -301,12 +288,6 @@ v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
- if (resource->secondaryScope) {
- v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, propertystring,
- QV8QObjectWrapper::IgnoreRevision);
- if (!result.IsEmpty()) return result;
- }
-
while (context) {
// Search context properties
if (context->propertyNames) {
@@ -408,12 +389,6 @@ v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property,
QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
- // Search scope object
- if (resource->secondaryScope &&
- qobjectWrapper->setProperty(resource->secondaryScope, propertystring, value,
- QV8QObjectWrapper::IgnoreRevision))
- return value;
-
while (context) {
// Search context properties
if (context->propertyNames && -1 != context->propertyNames->value(propertystring))
diff --git a/src/qml/qml/v8/qv8contextwrapper_p.h b/src/qml/qml/v8/qv8contextwrapper_p.h
index 117f16ab39..5c7b8c2187 100644
--- a/src/qml/qml/v8/qv8contextwrapper_p.h
+++ b/src/qml/qml/v8/qv8contextwrapper_p.h
@@ -79,13 +79,6 @@ public:
void addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script>,
QQmlContextData *ctxt);
- // XXX We only use the secondary scope to pass the "arguments" of the signal to
- // on<SignalName> properties. Instead of doing this we should rewrite the
- // JavaScript closure function to accept these arguments as named parameters.
- // To keep backwards compatibility we have to check that the argument names are
- // not members of the QV8Engine::illegalNames() set.
- QObject *setSecondaryScope(v8::Handle<v8::Object>, QObject *);
-
QQmlContextData *callingContext();
QQmlContextData *context(v8::Handle<v8::Value>);
diff --git a/src/quick/util/qquickconnections.cpp b/src/quick/util/qquickconnections.cpp
index d2591557d1..98fc270e13 100644
--- a/src/quick/util/qquickconnections.cpp
+++ b/src/quick/util/qquickconnections.cpp
@@ -279,8 +279,9 @@ void QQuickConnections::connectSignals()
QQmlProperty prop(target(), propName);
if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
+ int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
QQmlBoundSignal *signal =
- new QQmlBoundSignal(target(), QQmlPropertyPrivate::get(prop)->signalIndex(), this, qmlEngine(this));
+ new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this));
QString location;
QQmlContextData *ctxtdata = 0;
@@ -292,7 +293,9 @@ void QQuickConnections::connectSignals()
}
QQmlBoundSignalExpression *expression = ctxtdata ?
- new QQmlBoundSignalExpression(ctxtdata, 0, script, true, location, line, column) : 0;
+ new QQmlBoundSignalExpression(target(), signalIndex,
+ ctxtdata, this, script,
+ true, location, line, column) : 0;
signal->takeExpression(expression);
d->boundsignals += signal;
} else {
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index 48c00f1d51..ead1cd7c0d 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -342,7 +342,9 @@ void QQuickPropertyChangesPrivate::decode()
QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
handler->property = prop;
- handler->expression.take(new QQmlBoundSignalExpression(QQmlContextData::get(qmlContext(q)), object, expression, false, url.toString(), line, column));
+ handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(),
+ QQmlContextData::get(qmlContext(q)), object, expression,
+ false, url.toString(), line, column));
signalReplacements << handler;
} else if (isScript) { // binding
QString expression = data.toString();