aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2021-11-01 16:18:30 +0100
committerAndrei Golubev <andrei.golubev@qt.io>2021-11-17 18:04:41 +0100
commitdd153ad17475ba561b02c343dd9f0ce95a41390b (patch)
treed0dc368c5d5fbb3cc22192d02b2fb330620bfdf8 /tools/qmltc
parent590dd43c4d1bdb67a2db66081357390458f87de8 (diff)
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>
Diffstat (limited to 'tools/qmltc')
-rw-r--r--tools/qmltc/CMakeLists.txt1
-rw-r--r--tools/qmltc/qmltccodewriter.cpp14
-rw-r--r--tools/qmltc/qmltccompiler.cpp76
-rw-r--r--tools/qmltc/qmltccompiler.h1
-rw-r--r--tools/qmltc/qmltccompilerutils.h66
-rw-r--r--tools/qmltc/qmltcoutputir.h3
6 files changed, 154 insertions, 7 deletions
diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt
index 74f0ab5f84..64c071c583 100644
--- a/tools/qmltc/CMakeLists.txt
+++ b/tools/qmltc/CMakeLists.txt
@@ -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
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index dff4885604..df9d11a6f7 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -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);
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 0aeff12217..7c9c7b1734 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -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 &current, 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 &current, const QQmlJSMetaEnum &e)
@@ -220,4 +228,72 @@ void QmltcCompiler::compileEnum(QmltcType &current, 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 &current, 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 &param : 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
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index 8d2f360d75..94247fbfea 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -65,6 +65,7 @@ private:
void compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type);
void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
+ void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m);
bool hasErrors() const { return m_logger->hasErrors(); } // TODO: count warnings as errors?
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
diff --git a/tools/qmltc/qmltccompilerutils.h b/tools/qmltc/qmltccompilerutils.h
new file mode 100644
index 0000000000..7af21532b9
--- /dev/null
+++ b/tools/qmltc/qmltccompilerutils.h
@@ -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
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 067fb1bd43..cddbb8dada 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.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
};