diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-11-01 16:18:30 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2021-11-17 18:04:41 +0100 |
commit | dd153ad17475ba561b02c343dd9f0ce95a41390b (patch) | |
tree | d0dc368c5d5fbb3cc22192d02b2fb330620bfdf8 /tools/qmltc | |
parent | 590dd43c4d1bdb67a2db66081357390458f87de8 (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.txt | 1 | ||||
-rw-r--r-- | tools/qmltc/qmltccodewriter.cpp | 14 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 76 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.h | 1 | ||||
-rw-r--r-- | tools/qmltc/qmltccompilerutils.h | 66 | ||||
-rw-r--r-- | tools/qmltc/qmltcoutputir.h | 3 |
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 ¤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 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 ¤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, 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 }; |