summaryrefslogtreecommitdiffstats
path: root/tools/dumpcpp/moc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dumpcpp/moc.cpp')
-rw-r--r--tools/dumpcpp/moc.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/tools/dumpcpp/moc.cpp b/tools/dumpcpp/moc.cpp
new file mode 100644
index 0000000..e405b0a
--- /dev/null
+++ b/tools/dumpcpp/moc.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#include "moc.h"
+
+#include <QDir>
+#include <QMetaObject>
+#include <QMetaProperty>
+#include <QProcess>
+#include <QTemporaryFile>
+#include <QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+QByteArray setterName(const QByteArray &propertyName)
+{
+ QByteArray setter(propertyName);
+ if (isupper(setter.at(0))) {
+ setter = "Set" + setter;
+ } else {
+ setter[0] = char(toupper(setter[0]));
+ setter = "set" + setter;
+ }
+ return setter;
+}
+
+void formatCppEnum(QTextStream &str, const QMetaEnum &metaEnum)
+{
+ str << " enum " << metaEnum.name() << " {" << Qt::endl;
+ for (int k = 0, last = metaEnum.keyCount() - 1; k <= last; ++k) {
+ QByteArray key(metaEnum.key(k));
+ str << " " << key.leftJustified(24) << "= " << metaEnum.value(k);
+ if (k < last)
+ str << ',';
+ str << Qt::endl;
+ }
+ str << " };" << Qt::endl;
+}
+
+void formatCppEnums(QTextStream &str, const QMetaObject *mo,
+ const char *qEnumDecl = nullptr /* Q_ENUM, Q_ENUM_NS */)
+{
+ const int offset = mo->enumeratorOffset();
+ const int count = mo->enumeratorCount();
+ for (int e = offset; e < count; ++e) {
+ const auto me = mo->enumerator(e);
+ formatCppEnum(str, me);
+ if (qEnumDecl)
+ str << " " << qEnumDecl << '(' << me.name() << ")\n";
+ str << '\n';
+ }
+ if (offset < count)
+ str << '\n';
+}
+
+static void formatCppMethods(QTextStream &str, const QMetaObject *mo,
+ QMetaMethod::MethodType filter)
+{
+ for (int m = mo->methodOffset(), count = mo->methodCount(); m < count; ++m) {
+ const auto &mt = mo->method(m);
+ if (mt.methodType() == filter)
+ str << " " << mt.typeName() << ' ' << mt.methodSignature() << ";\n";
+ }
+}
+
+static void formatCppProperty(QTextStream &str, const QMetaProperty &p)
+{
+ str << " Q_PROPERTY(" << p.typeName() << ' ' << p.name()
+ << " READ " << p.name();
+ if (p.isWritable())
+ str << " WRITE " << setterName(p.name());
+ if (p.hasNotifySignal())
+ str << " NOTIFY " << p.notifySignal().name();
+ if (p.isUser())
+ str << " USER true";
+ if (!p.isDesignable())
+ str << " DESIGNABLE false";
+ if (!p.isStored())
+ str << " STORED false";
+ if (p.isFinal())
+ str << " FINAL";
+ str << ")\n";
+}
+
+static void formatCppQuotedString(QTextStream &str, const char *s)
+{
+ str << '"';
+ for ( ; *s ; ++s) {
+ const char c = *s;
+ if (c == '\\' || c == '\"')
+ str << '\\';
+ str << c;
+ }
+ str << '"';
+}
+
+// Generate C++ code from an ActiveQt QMetaObject to be parsed by moc
+static QString mocHeader(const QMetaObject *mo, const QStringList &name,
+ const QString &baseClass)
+{
+ QString result;
+ QTextStream str(&result);
+
+ str << "#pragma once\n\n";
+ if (!baseClass.isEmpty())
+ str << "#include <" << baseClass << ">\n";
+ str << "#include <qt_windows.h>\n\n";
+
+ for (int n = 0, count = name.size() - 1; n < count; ++n)
+ str << "namespace " << name.at(n) << " {\n";
+
+ str << "\nclass " << name.constLast();
+ if (!baseClass.isEmpty())
+ str << " : public " << baseClass;
+ str<< "\n{\n Q_OBJECT\n";
+
+ for (int i = mo->classInfoOffset(), count = mo->classInfoCount(); i < count; ++i) {
+ const auto &info = mo->classInfo(i);
+ str << " Q_CLASSINFO(";
+ formatCppQuotedString(str, info.name());
+ str << ", ";
+ formatCppQuotedString(str, info.value());
+ str << ")\n";
+ }
+
+ for (int p = mo->propertyOffset(), count = mo-> propertyCount(); p < count; ++p)
+ formatCppProperty(str, mo->property(p));
+
+ str << "public:\n";
+
+ formatCppEnums(str, mo, "Q_ENUM");
+
+ formatCppMethods(str, mo, QMetaMethod::Constructor);
+ str << "\nQ_SIGNALS:\n";
+ formatCppMethods(str, mo, QMetaMethod::Signal);
+ str << "\npublic Q_SLOTS:\n";
+ formatCppMethods(str, mo, QMetaMethod::Slot);
+ str << "};\n";
+
+ for (int n = name.size() - 1; n >= 0 ; --n)
+ str << "} // namespace " << name.at(n) << '\n';
+
+ return result;
+}
+
+static QString processOutput(QByteArray output)
+{
+ for (int c = output.size() - 1; c >= 0; --c) {
+ if (output.at(c) == '\r')
+ output.remove(c, 1);
+ }
+ return QString::fromUtf8(output);
+}
+
+static QString runProcess(const QString &binary, const QStringList &args,
+ QString *errorString)
+{
+ QProcess process;
+ process.start(binary, args);
+ if (!process.waitForStarted()) {
+ *errorString = QLatin1String("Cannot start ") + binary + QLatin1String(": ") + process.errorString();
+ return QString();
+ }
+ if (!process.waitForFinished()) {
+ *errorString = binary + QLatin1String(" timed out: ") + process.errorString();
+ return QString();
+ }
+ if (process.exitStatus() != QProcess::NormalExit) {
+ *errorString = binary + QLatin1String(" crashed: ") + process.errorString();
+ return QString();
+ }
+ if (process.exitCode() != 0) {
+ *errorString = binary + QLatin1String(" failed: ") + processOutput(process.readAllStandardError());
+ return QString();
+ }
+ return processOutput(process.readAllStandardOutput());
+}
+
+static int lineStart(int pos, const QString *s)
+{
+ const int lineStart = s->lastIndexOf(QLatin1Char('\n'), pos);
+ return lineStart >= 0 ? lineStart + 1 : 0;
+}
+
+static int nextLineFeed(int pos, const QString *s)
+{
+ const int nextLineStart = s->indexOf(QLatin1Char('\n'), pos);
+ return nextLineStart >= 0 ? nextLineStart : s->size();
+}
+
+static void removeLines(const QString &start, const QString &end,
+ QString *s, bool keepEnd = false)
+{
+ int startPos = s->indexOf(start);
+ if (startPos < 0)
+ return;
+ int endPos = s->indexOf(end, startPos + start.size());
+ if (endPos < 0)
+ return;
+
+ startPos = lineStart(startPos, s);
+ endPos = keepEnd
+ ? lineStart(endPos, s)
+ : nextLineFeed(endPos + end.size(), s);
+ s->remove(startPos, endPos - startPos);
+}
+
+static QString cleanCode(QString code, const QString &className, const QString &headerFileName)
+{
+ // remove include of temp file
+ code.remove(QLatin1String("#include \"") + headerFileName + QLatin1String("\"\n"));
+
+ const char *removeFunctions[] = {"metaObject", "qt_metacall", "qt_static_metacall"};
+
+ const QString funcStart = className + QLatin1String("::");
+ const QString nextFuncStart = QLatin1String("\n}");
+ for (auto function : removeFunctions)
+ removeLines(funcStart + QLatin1String(function) + QLatin1Char('('), nextFuncStart, &code);
+
+ // qt_static_metacall is not implemented, cannot access private function of QAxObject
+ code.replace(QLatin1String(" qt_static_metacall,"), QLatin1String(" nullptr,"));
+
+ // Remove internal signals
+ removeLines(QLatin1String("// SIGNAL 0"), QLatin1String("QT_WARNING_POP"), &code, true);
+
+ // Fix enum uint(Namespace::Class::Value) -> uint(Namespace::Value) (dumpcpp convention)
+ const QString enumPrefix = QLatin1String("uint(");
+ QString parentName = className;
+ const int lastSep = parentName.lastIndexOf(QLatin1String("::"));
+ if (lastSep >= 0)
+ parentName.truncate(lastSep);
+ else
+ parentName.clear();
+ code.replace(enumPrefix + className + QLatin1String("::"),
+ enumPrefix + parentName + QLatin1String("::"));
+ return code;
+}
+
+QString mocCode(const QMetaObject *mo, const QString &qualifiedClassName,
+ QString baseClass, QString *errorString)
+{
+ QStringList name = qualifiedClassName.split(QLatin1String("::"));
+ if (name.isEmpty())
+ name.append(QLatin1String(mo->className()));
+
+ if (baseClass.isEmpty()) {
+ if (const auto sc = mo->superClass())
+ baseClass = QLatin1String(sc->className());
+ }
+
+ const QString tempPattern = QDir::tempPath() + QLatin1Char('/')
+ + name.constLast().toLower() + QLatin1String("_XXXXXX.h");
+ QTemporaryFile header(tempPattern);
+ if (!header.open()) {
+ *errorString = QLatin1String("Cannot open temporary file: ") + header.errorString();
+ return QString();
+ }
+ const QString headerCode = mocHeader(mo, name, baseClass);
+ header.write(headerCode.toUtf8());
+ const QString headerFileName = header.fileName();
+ header.close();
+
+ const QString binary = QLatin1String("moc.exe");
+
+ QString result = runProcess(binary, {header.fileName()}, errorString);
+ if (result.isEmpty()) {
+ errorString->append(QLatin1String("\n\nOffending code:\n"));
+ errorString->append(headerCode);
+ return result;
+ }
+
+ return cleanCode(result, name.join(QLatin1String("::")), headerFileName);
+}
+
+QT_END_NAMESPACE