Fix use of function expressions with signal handlers
authorSimon Hausmann <simon.hausmann@theqtcompany.com>
Sun, 16 Aug 2015 13:26:40 +0000 (15:26 +0200)
committerSimon Hausmann <simon.hausmann@theqtcompany.com>
Mon, 24 Aug 2015 08:12:47 +0000 (08:12 +0000)
Writing

    onClicked: function(mouseEvent) { ... }

would get silently "accepted" by the engine, but it wouldn't do anything. We
basically wrapped it in a new function, so that it became

    onClicked: function(mouse){ function(mouseEvent() {} }

which is a noop. With older versions this used to produce a syntax error.
However the better fix is to simply support this kind of assignment for more
expressive signal handlers, because now the names of the signal parameters can
be explicitly named (with names of your choice).

Change-Id: I96369f8805fab97509784222f614ee17cf681aba
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
Reviewed-by: Michael Brasser <michael.brasser@live.com>
src/qml/compiler/qqmltypecompiler.cpp
tests/auto/qml/qqmllanguage/data/assignSignalFunctionExpression.qml [new file with mode: 0644]
tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp

index 80aa617c5362b81efafdcf1e224bd12c99d49bab..c02b91f30037d94406f774f6d120e1f9585c6271 100644 (file)
@@ -1046,16 +1046,29 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
             paramList = paramList->finish();
 
         QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
-        QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
-        QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
-        QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
-        elements = elements->finish();
-
-        QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
+        QQmlJS::AST::FunctionDeclaration *functionDeclaration = 0;
+        if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) {
+            if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) {
+                functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body);
+                functionDeclaration->functionToken = fe->functionToken;
+                functionDeclaration->identifierToken = fe->identifierToken;
+                functionDeclaration->lparenToken = fe->lparenToken;
+                functionDeclaration->rparenToken = fe->rparenToken;
+                functionDeclaration->lbraceToken = fe->lbraceToken;
+                functionDeclaration->rbraceToken = fe->rbraceToken;
+            }
+        }
+        if (!functionDeclaration) {
+            QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
+            QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
+            QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
+            elements = elements->finish();
 
-        QQmlJS::AST::FunctionDeclaration *functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
-        functionDeclaration->functionToken = foe->node->firstSourceLocation();
+            QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
 
+            functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
+            functionDeclaration->functionToken = foe->node->firstSourceLocation();
+        }
         foe->node = functionDeclaration;
         binding->propertyNameIndex = compiler->registerString(propertyName);
         binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
diff --git a/tests/auto/qml/qqmllanguage/data/assignSignalFunctionExpression.qml b/tests/auto/qml/qqmllanguage/data/assignSignalFunctionExpression.qml
new file mode 100644 (file)
index 0000000..bf8f855
--- /dev/null
@@ -0,0 +1,5 @@
+import Test 1.0
+MyQmlObject {
+    onBasicSignal: function() { basicSlot() }
+    onBasicParameterizedSignal: function(param) { basicSlotWithArgs(param) }
+}
index 152b7510d28686c8293bf3baa55089646d8dd09f..97501118dd85ca5ddbfe872e3cbccc5409c2efb2 100644 (file)
@@ -117,6 +117,7 @@ private slots:
     void idProperty();
     void autoNotifyConnection();
     void assignSignal();
+    void assignSignalFunctionExpression();
     void overrideSignal_data();
     void overrideSignal();
     void dynamicProperties();
@@ -1263,6 +1264,17 @@ void tst_qqmllanguage::assignSignal()
     emit object->basicParameterizedSignal(9);
 }
 
+void tst_qqmllanguage::assignSignalFunctionExpression()
+{
+    QQmlComponent component(&engine, testFileUrl("assignSignalFunctionExpression.qml"));
+    VERIFY_ERRORS(0);
+    MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+    QVERIFY(object != 0);
+    QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot");
+    emit object->basicSignal();
+    QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlotWithArgs(9)");
+    emit object->basicParameterizedSignal(9);
+}
 
 void tst_qqmllanguage::overrideSignal_data()
 {