aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/qml/qqmlecmascript/data/signalArguments.1.qml11
-rw-r--r--tests/auto/qml/qqmlecmascript/data/signalArguments.2.qml14
-rw-r--r--tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h4
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp37
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp39
-rw-r--r--tests/auto/qml/qquickconnection/data/rewriteError-global.qml8
-rw-r--r--tests/auto/qml/qquickconnection/data/rewriteError-unnamed.qml8
-rw-r--r--tests/auto/qml/qquickconnection/tst_qquickconnection.cpp51
25 files changed, 621 insertions, 313 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();
diff --git a/tests/auto/qml/qqmlecmascript/data/signalArguments.1.qml b/tests/auto/qml/qqmlecmascript/data/signalArguments.1.qml
new file mode 100644
index 0000000000..3ab714b800
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/signalArguments.1.qml
@@ -0,0 +1,11 @@
+import Qt.test 1.0
+
+MyQmlObject {
+ property int argumentCount: -1
+ property bool calleeCorrect: false
+ onBasicSignal: {
+ argumentCount = arguments.length
+ calleeCorrect = (arguments.callee === onBasicSignal)
+ setString('pass')
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/signalArguments.2.qml b/tests/auto/qml/qqmlecmascript/data/signalArguments.2.qml
new file mode 100644
index 0000000000..8ecb8df6ee
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/signalArguments.2.qml
@@ -0,0 +1,14 @@
+import Qt.test 1.0
+
+MyQmlObject {
+ property int argumentCount: -1
+ property bool calleeCorrect: false
+
+ onArgumentSignal: {
+ argumentCount = arguments.length
+ calleeCorrect = (arguments.callee === onArgumentSignal)
+ setString('pass ' + arguments[0] + ' ' + arguments[1] + ' '
+ + arguments[2] + ' ' + arguments[3] + ' '
+ + arguments[4])
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml b/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml
index 49293edfb3..5b73430aa3 100644
--- a/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml
+++ b/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml
@@ -2,4 +2,5 @@ import Qt.test 1.0
MyQmlObject {
onSignalWithUnknownType: variantMethod(arg);
+ onSignalWithCompletelyUnknownType: variantMethod(arg)
}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index da6baa4e6c..2fc0568fda 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -193,6 +193,9 @@ public:
struct MyType {
int value;
};
+ struct MyOtherType {
+ int value;
+ };
QVariant variant() const { return m_variant; }
QJSValue qjsvalue() const { return m_qjsvalue; }
void setQJSValue(const QJSValue &value) { m_qjsvalue = value; emit qjsvalueChanged(); }
@@ -247,6 +250,7 @@ signals:
void anotherBasicSignal();
void thirdBasicSignal();
void signalWithUnknownType(const MyQmlObject::MyType &arg);
+ void signalWithCompletelyUnknownType(const MyQmlObject::MyOtherType &arg);
void signalWithVariant(const QVariant &arg);
void signalWithQJSValue(const QJSValue &arg);
void signalWithGlobalName(int parseInt);
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 196587160b..2ea91add2f 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -79,6 +79,7 @@ private slots:
void boolPropertiesEvaluateAsBool();
void methods();
void signalAssignment();
+ void signalArguments();
void bindingLoop();
void basicExpressions();
void basicExpressions_data();
@@ -513,21 +514,42 @@ void tst_qqmlecmascript::signalAssignment()
{
QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
+ QVERIFY(component.isError());
+ QString expectedErrorString = component.url().toString() + QLatin1String(":4 Signal uses unnamed parameter followed by named parameter.\n");
+ QCOMPARE(component.errorString(), expectedErrorString);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
+ QVERIFY(component.isError());
+ QString expectedErrorString = component.url().toString() + QLatin1String(":5 Signal parameter \"parseInt\" hides global variable.\n");
+ QCOMPARE(component.errorString(), expectedErrorString);
+ }
+}
+
+void tst_qqmlecmascript::signalArguments()
+{
+ {
+ QQmlComponent component(&engine, testFileUrl("signalArguments.1.qml"));
MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->string(), QString());
- emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
- QCOMPARE(object->string(), QString("pass 19 Hello world!"));
+ emit object->basicSignal();
+ QCOMPARE(object->string(), QString("pass"));
+ QCOMPARE(object->property("argumentCount").toInt(), 0);
+ QCOMPARE(object->property("calleeCorrect").toBool(), true);
delete object;
}
{
- QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
+ QQmlComponent component(&engine, testFileUrl("signalArguments.2.qml"));
MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->string(), QString());
- emit object->signalWithGlobalName(19);
- QCOMPARE(object->string(), QString("pass 5"));
+ emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
+ QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
+ QCOMPARE(object->property("argumentCount").toInt(), 5);
+ QCOMPARE(object->property("calleeCorrect").toBool(), true);
delete object;
}
}
@@ -3432,6 +3454,11 @@ void tst_qqmlecmascript::signalWithUnknownTypes()
QCOMPARE(result.value, type.value);
+ MyQmlObject::MyOtherType othertype;
+ othertype.value = 17;
+ emit object->signalWithCompletelyUnknownType(othertype);
+
+ QVERIFY(!object->variant().isValid());
delete object;
}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index b8e47c80ec..fdb6d69075 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -49,6 +49,7 @@
#include <QtWidgets/QLineEdit>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
+#include <QtCore/private/qobject_p.h>
#include "../../shared/util.h"
#include <QDebug>
@@ -146,14 +147,14 @@ void tst_qqmlproperty::qmlmetaproperty()
{
QQmlProperty prop;
+ QObject *obj = new QObject;
+
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
- QObject *obj = new QObject;
-
QCOMPARE(prop.name(), QString());
QCOMPARE(prop.read(), QVariant());
QCOMPARE(prop.write(QVariant()), false);
@@ -366,7 +367,7 @@ void tst_qqmlproperty::qmlmetaproperty_object()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -414,7 +415,7 @@ void tst_qqmlproperty::qmlmetaproperty_object()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -469,7 +470,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -517,7 +518,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -567,7 +568,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -616,7 +617,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -670,7 +671,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -718,7 +719,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -773,7 +774,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -821,7 +822,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -871,7 +872,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -920,7 +921,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding != 0);
- QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
+ QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1);
QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted());
@@ -1102,7 +1103,7 @@ void tst_qqmlproperty::read()
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.read(), QVariant());
- QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
+ QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.read(), QVariant());
@@ -1114,7 +1115,7 @@ void tst_qqmlproperty::read()
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.read(), QVariant());
- QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
+ QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.read(), QVariant());
@@ -1270,7 +1271,7 @@ void tst_qqmlproperty::write()
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
- QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
+ QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
@@ -1284,7 +1285,7 @@ void tst_qqmlproperty::write()
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
- QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
+ QVERIFY(0 == QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1)));
QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
diff --git a/tests/auto/qml/qquickconnection/data/rewriteError-global.qml b/tests/auto/qml/qquickconnection/data/rewriteError-global.qml
new file mode 100644
index 0000000000..bd18b9df9a
--- /dev/null
+++ b/tests/auto/qml/qquickconnection/data/rewriteError-global.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+import Test 1.0
+
+TestObject {
+ property QtObject connection: Connections {
+ onSignalWithGlobalName: { ran = true }
+ }
+}
diff --git a/tests/auto/qml/qquickconnection/data/rewriteError-unnamed.qml b/tests/auto/qml/qquickconnection/data/rewriteError-unnamed.qml
new file mode 100644
index 0000000000..a4849e994b
--- /dev/null
+++ b/tests/auto/qml/qquickconnection/data/rewriteError-unnamed.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+import Test 1.0
+
+TestObject {
+ property QtObject connection: Connections {
+ onUnnamedArgumentSignal: { ran = true }
+ }
+}
diff --git a/tests/auto/qml/qquickconnection/tst_qquickconnection.cpp b/tests/auto/qml/qquickconnection/tst_qquickconnection.cpp
index 118d89e41e..9796872035 100644
--- a/tests/auto/qml/qquickconnection/tst_qquickconnection.cpp
+++ b/tests/auto/qml/qquickconnection/tst_qquickconnection.cpp
@@ -62,6 +62,7 @@ private slots:
void unknownSignals();
void errors_data();
void errors();
+ void rewriteErrors();
void singletonTypeTarget();
private:
@@ -224,6 +225,56 @@ void tst_qquickconnection::errors()
QCOMPARE(errors.at(0).description(), error);
}
+class TestObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool ran READ ran WRITE setRan)
+
+public:
+ TestObject(QObject *parent = 0) : m_ran(false) {}
+ ~TestObject() {}
+
+ bool ran() const { return m_ran; }
+ void setRan(bool arg) { m_ran = arg; }
+
+signals:
+ void unnamedArgumentSignal(int a, qreal, QString c);
+ void signalWithGlobalName(int parseInt);
+
+private:
+ bool m_ran;
+};
+
+void tst_qquickconnection::rewriteErrors()
+{
+ qmlRegisterType<TestObject>("Test", 1, 0, "TestObject");
+ {
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("rewriteError-unnamed.qml"));
+ TestObject *obj = qobject_cast<TestObject*>(c.create());
+ QVERIFY(obj != 0);
+
+ QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1());
+ obj->unnamedArgumentSignal(1, .5, "hello");
+ QCOMPARE(obj->ran(), false);
+
+ delete obj;
+ }
+
+ {
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("rewriteError-global.qml"));
+ TestObject *obj = qobject_cast<TestObject*>(c.create());
+ QVERIFY(obj != 0);
+
+ QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1());
+ obj->signalWithGlobalName(10);
+ QCOMPARE(obj->ran(), false);
+
+ delete obj;
+ }
+}
+
class MyTestSingletonType : public QObject
{