Fix use of function expressions with signal handlers

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>
This commit is contained in:
Simon Hausmann 2015-08-16 15:26:40 +02:00
parent 45ea8df187
commit c907eb1b5b
3 changed files with 38 additions and 8 deletions

View 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::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::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
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;

View File

@ -0,0 +1,5 @@
import Test 1.0
MyQmlObject {
onBasicSignal: function() { basicSlot() }
onBasicParameterizedSignal: function(param) { basicSlotWithArgs(param) }
}

View 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()
{