qmltc: Compile QML methods to C++
This also requires some changes to the output IR + couple things could also be fixed while at it Task-number: QTBUG-84368 Change-Id: Ie1535cbbe36cd874e9787ea91fe85b638326de20 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
590dd43c4d
commit
dd153ad174
|
@ -10,6 +10,7 @@ set(qml_sources
|
|||
data/NameConflict.qml
|
||||
data/simpleQtQuickTypes.qml
|
||||
data/typeWithEnums.qml
|
||||
data/methods.qml
|
||||
)
|
||||
|
||||
set_source_files_properties(data/NameConflict.qml PROPERTIES
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import QtQml
|
||||
QtObject {
|
||||
signal justSignal()
|
||||
signal typedSignal(string a, QtObject b, real c)
|
||||
|
||||
function justMethod() {
|
||||
console.log("justMethod()");
|
||||
}
|
||||
|
||||
function untypedMethod(d, c) {
|
||||
console.log("methodWithParams, d = " + d + ", c = " + c);
|
||||
}
|
||||
|
||||
function typedMethod(a: real, b: int): string {
|
||||
console.log("typedMethod, a = " + a + ", b = " + b);
|
||||
return a + b;
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
#include "helloworld.h"
|
||||
#include "simpleqtquicktypes.h"
|
||||
#include "typewithenums.h"
|
||||
#include "methods.h"
|
||||
|
||||
// Qt:
|
||||
#include <QtCore/qstring.h>
|
||||
|
@ -143,4 +144,48 @@ void tst_qmltc::enumerations()
|
|||
QCOMPARE(enumerator2.value(2), PREPEND_NAMESPACE(typeWithEnums)::ValuesSpecified::B2_);
|
||||
}
|
||||
|
||||
void tst_qmltc::methods()
|
||||
{
|
||||
QQmlEngine e;
|
||||
PREPEND_NAMESPACE(methods) created(&e);
|
||||
|
||||
const QMetaObject *mo = created.metaObject();
|
||||
QVERIFY(mo);
|
||||
|
||||
QMetaMethod metaJustSignal = mo->method(mo->indexOfSignal("justSignal()"));
|
||||
QMetaMethod metaTypedSignal = mo->method(mo->indexOfSignal(
|
||||
QMetaObject::normalizedSignature("typedSignal(QString,QObject *,double)")));
|
||||
QMetaMethod metaJustMethod = mo->method(mo->indexOfMethod("justMethod()"));
|
||||
QMetaMethod metaUntypedMethod = mo->method(mo->indexOfMethod(
|
||||
QMetaObject::normalizedSignature("untypedMethod(QVariant,QVariant)")));
|
||||
QMetaMethod metaTypedMethod = mo->method(
|
||||
mo->indexOfMethod(QMetaObject::normalizedSignature("typedMethod(double,int)")));
|
||||
|
||||
QVERIFY(metaJustSignal.isValid());
|
||||
QVERIFY(metaTypedSignal.isValid());
|
||||
QVERIFY(metaJustMethod.isValid());
|
||||
QVERIFY(metaUntypedMethod.isValid());
|
||||
QVERIFY(metaTypedMethod.isValid());
|
||||
|
||||
QCOMPARE(metaJustSignal.methodType(), QMetaMethod::Signal);
|
||||
QCOMPARE(metaTypedSignal.methodType(), QMetaMethod::Signal);
|
||||
QCOMPARE(metaJustMethod.methodType(), QMetaMethod::Method);
|
||||
QCOMPARE(metaUntypedMethod.methodType(), QMetaMethod::Method);
|
||||
QCOMPARE(metaTypedMethod.methodType(), QMetaMethod::Method);
|
||||
|
||||
QCOMPARE(metaTypedSignal.parameterMetaType(0), QMetaType::fromType<QString>());
|
||||
QCOMPARE(metaTypedSignal.parameterMetaType(1), QMetaType::fromType<QObject *>());
|
||||
QCOMPARE(metaTypedSignal.parameterMetaType(2), QMetaType::fromType<double>());
|
||||
QCOMPARE(metaTypedSignal.parameterNames(), QList<QByteArray>({ "a", "b", "c" }));
|
||||
|
||||
QCOMPARE(metaUntypedMethod.parameterMetaType(0), QMetaType::fromType<QVariant>());
|
||||
QCOMPARE(metaUntypedMethod.parameterMetaType(1), QMetaType::fromType<QVariant>());
|
||||
QCOMPARE(metaUntypedMethod.parameterNames(), QList<QByteArray>({ "d", "c" }));
|
||||
|
||||
QCOMPARE(metaTypedMethod.parameterMetaType(0), QMetaType::fromType<double>());
|
||||
QCOMPARE(metaTypedMethod.parameterMetaType(1), QMetaType::fromType<int>());
|
||||
QCOMPARE(metaTypedMethod.returnMetaType(), QMetaType::fromType<QString>());
|
||||
QCOMPARE(metaTypedMethod.parameterNames(), QList<QByteArray>({ "a", "b" }));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qmltc)
|
||||
|
|
|
@ -48,4 +48,5 @@ private slots:
|
|||
void helloWorld();
|
||||
void qtQuickIncludes();
|
||||
void enumerations();
|
||||
void methods();
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ qt_internal_add_tool(${target_name}
|
|||
qmltctyperesolver.h
|
||||
qmltcvisitor.h qmltcvisitor.cpp
|
||||
qmltccompiler.h qmltccompiler.cpp
|
||||
qmltccompilerutils.h
|
||||
DEFINES
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
|
|
|
@ -309,7 +309,10 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
|
|||
{
|
||||
const auto [hSignature, cppSignature] = functionSignatures(method);
|
||||
// Note: augment return type with preambles in declaration
|
||||
code.rawAppendToHeader(method.returnType + u" " + hSignature + u";");
|
||||
QString prefix = method.declarationPrefixes.join(u' ');
|
||||
if (!prefix.isEmpty())
|
||||
prefix.append(u' ');
|
||||
code.rawAppendToHeader(prefix + method.returnType + u" " + hSignature + u";");
|
||||
|
||||
// do not generate method implementation if it is a signal
|
||||
const auto methodType = method.type;
|
||||
|
@ -331,13 +334,12 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
|
|||
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
|
||||
{
|
||||
const auto [hSignature, cppSignature] = functionSignatures(ctor);
|
||||
const QString returnTypeWithSpace = ctor.returnType.isEmpty() ? u""_qs : ctor.returnType + u" ";
|
||||
|
||||
code.rawAppendToHeader(returnTypeWithSpace + hSignature + u";");
|
||||
QString prefix = ctor.declarationPrefixes.join(u' ');
|
||||
if (!prefix.isEmpty())
|
||||
prefix.append(u' ');
|
||||
code.rawAppendToHeader(prefix + hSignature + u";");
|
||||
|
||||
code.rawAppendToCpp(u""); // blank line
|
||||
if (!returnTypeWithSpace.isEmpty())
|
||||
code.rawAppendToCpp(returnTypeWithSpace);
|
||||
code.rawAppendSignatureToCpp(cppSignature);
|
||||
if (!ctor.initializerList.isEmpty()) {
|
||||
code.rawAppendToCpp(u":", 1);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "qmltccompiler.h"
|
||||
#include "qmltcoutputir.h"
|
||||
#include "qmltccodewriter.h"
|
||||
#include "qmltccompilerutils.h"
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -205,6 +207,12 @@ void QmltcCompiler::compileType(QmltcType ¤t, const QQmlJSScope::ConstPtr
|
|||
current.enums.reserve(enums.size());
|
||||
for (auto it = enums.begin(); it != enums.end(); ++it)
|
||||
compileEnum(current, it.value());
|
||||
|
||||
const auto methods = type->ownMethods();
|
||||
const auto properties = type->ownProperties();
|
||||
current.functions.reserve(methods.size() + properties.size() * 3); // sensible default
|
||||
for (const QQmlJSMetaMethod &m : methods)
|
||||
compileMethod(current, m);
|
||||
}
|
||||
|
||||
void QmltcCompiler::compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e)
|
||||
|
@ -220,4 +228,72 @@ void QmltcCompiler::compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e)
|
|||
u"Q_ENUM(%1)"_qs.arg(e.name()));
|
||||
}
|
||||
|
||||
static QList<QmltcVariable>
|
||||
compileMethodParameters(const QStringList &names,
|
||||
const QList<QSharedPointer<const QQmlJSScope>> &types,
|
||||
bool allowUnnamed = false)
|
||||
{
|
||||
QList<QmltcVariable> parameters;
|
||||
const auto size = names.size();
|
||||
parameters.reserve(size);
|
||||
for (qsizetype i = 0; i < size; ++i) {
|
||||
Q_ASSERT(types[i]); // assume verified
|
||||
QString name = names[i];
|
||||
Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified
|
||||
if (name.isEmpty() && allowUnnamed)
|
||||
name = u"unnamed_" + QString::number(i);
|
||||
parameters.emplaceBack(augmentInternalName(types[i]), name, QString());
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
void QmltcCompiler::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m)
|
||||
{
|
||||
const auto figureReturnType = [](const QQmlJSMetaMethod &m) {
|
||||
const bool isVoidMethod =
|
||||
m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal;
|
||||
Q_ASSERT(isVoidMethod || m.returnType());
|
||||
QString type;
|
||||
if (isVoidMethod) {
|
||||
type = u"void"_qs;
|
||||
} else {
|
||||
type = augmentInternalName(m.returnType());
|
||||
}
|
||||
return type;
|
||||
};
|
||||
|
||||
const auto returnType = figureReturnType(m);
|
||||
const auto paramNames = m.parameterNames();
|
||||
const auto paramTypes = m.parameterTypes();
|
||||
Q_ASSERT(paramNames.size() == paramTypes.size()); // assume verified
|
||||
const QList<QmltcVariable> compiledParams = compileMethodParameters(paramNames, paramTypes);
|
||||
const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
|
||||
|
||||
QStringList code;
|
||||
if (methodType != QQmlJSMetaMethod::Signal) {
|
||||
// just put "unimplemented" for now
|
||||
for (const QmltcVariable ¶m : compiledParams)
|
||||
code << u"Q_UNUSED(%1);"_qs.arg(param.name);
|
||||
code << u"Q_UNIMPLEMENTED();"_qs;
|
||||
|
||||
if (returnType != u"void"_qs) {
|
||||
code << u"return %1;"_qs.arg(m.returnType()->accessSemantics()
|
||||
== QQmlJSScope::AccessSemantics::Reference
|
||||
? u"nullptr"_qs
|
||||
: returnType + u"{}");
|
||||
}
|
||||
}
|
||||
|
||||
QmltcMethod compiled {};
|
||||
compiled.returnType = returnType;
|
||||
compiled.name = m.methodName();
|
||||
compiled.parameterList = std::move(compiledParams);
|
||||
compiled.body = std::move(code);
|
||||
compiled.type = methodType;
|
||||
compiled.access = m.access();
|
||||
if (methodType == QQmlJSMetaMethod::Method)
|
||||
compiled.declarationPrefixes << u"Q_INVOKABLE"_qs;
|
||||
current.functions.emplaceBack(compiled);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -65,6 +65,7 @@ private:
|
|||
|
||||
void compileType(QmltcType ¤t, const QQmlJSScope::ConstPtr &type);
|
||||
void compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e);
|
||||
void compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m);
|
||||
|
||||
bool hasErrors() const { return m_logger->hasErrors(); } // TODO: count warnings as errors?
|
||||
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the tools applications of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLTCCOMPILERUTILS_H
|
||||
#define QMLTCCOMPILERUTILS_H
|
||||
|
||||
#include <QtCore/qstring.h>
|
||||
#include <private/qqmljsscope_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Wraps \a type into \c const and \c & if that is a "good" thing to do (e.g.
|
||||
the type is not a pointer type).
|
||||
*/
|
||||
inline QString wrapInConstRef(QString type)
|
||||
{
|
||||
if (!type.endsWith(u'*'))
|
||||
type = u"const " + type + u"&";
|
||||
return type;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Returns an internalName() of \a s, using the accessSemantics() to augment
|
||||
the result
|
||||
*/
|
||||
inline QString augmentInternalName(const QQmlJSScope::ConstPtr &s)
|
||||
{
|
||||
Q_ASSERT(s->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence);
|
||||
const QString suffix =
|
||||
(s->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) ? u" *"_qs : u""_qs;
|
||||
return s->internalName() + suffix;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMLTCCOMPILERUTILS_H
|
|
@ -75,16 +75,17 @@ struct QmltcEnum
|
|||
|
||||
struct QmltcMethodBase
|
||||
{
|
||||
QString returnType; // C++ return type
|
||||
QString name; // C++ function name
|
||||
QList<QmltcVariable> parameterList; // C++ function parameter list
|
||||
QStringList body; // C++ function code
|
||||
QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
|
||||
QStringList declarationPrefixes;
|
||||
};
|
||||
|
||||
// Represents QML -> C++ compiled function
|
||||
struct QmltcMethod : QmltcMethodBase
|
||||
{
|
||||
QString returnType; // C++ return type
|
||||
QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue