diff options
-rw-r--r-- | src/activeqt/container/qaxbase.cpp | 3 | ||||
-rw-r--r-- | tests/auto/dumpcpp/tst_dumpcpp.cpp | 1 | ||||
-rw-r--r-- | tools/dumpcpp/dumpcpp.pro | 3 | ||||
-rw-r--r-- | tools/dumpcpp/main.cpp | 521 | ||||
-rw-r--r-- | tools/dumpcpp/moc.cpp | 300 | ||||
-rw-r--r-- | tools/dumpcpp/moc.h | 48 |
6 files changed, 386 insertions, 490 deletions
diff --git a/src/activeqt/container/qaxbase.cpp b/src/activeqt/container/qaxbase.cpp index ab661a4..c1b38e2 100644 --- a/src/activeqt/container/qaxbase.cpp +++ b/src/activeqt/container/qaxbase.cpp @@ -3452,7 +3452,6 @@ int QAxBase::internalProperty(QMetaObject::Call call, int index, void **v) if (dispid == DISPID_UNKNOWN) return index; - Q_ASSERT(d->metaobj); // property found, so everthing that goes wrong now should not bother the caller index -= mo->propertyCount(); @@ -3555,8 +3554,6 @@ int QAxBase::internalInvoke(QMetaObject::Call call, int index, void **v) const QMetaObjectExtra &moExtra = moextra_cache.value(d->metaObject()); DISPID dispid = moExtra.dispIDofName(slotname, disp); - Q_ASSERT(d->metaobj); - if (dispid == DISPID_UNKNOWN && slotname.toLower().startsWith("set")) { // see if we are calling a property set function as a slot slotname.remove(0, 3); diff --git a/tests/auto/dumpcpp/tst_dumpcpp.cpp b/tests/auto/dumpcpp/tst_dumpcpp.cpp index cf542f3..039c090 100644 --- a/tests/auto/dumpcpp/tst_dumpcpp.cpp +++ b/tests/auto/dumpcpp/tst_dumpcpp.cpp @@ -41,7 +41,6 @@ private slots: // A simple test to verify that an object can be instantiated and interacted with void tst_dumpcpp::toggleAddressBar() { - QSKIP("Crashes in Qt 6 pending rewrite of dumpcpp for new QMetaObject", Abort); // Qt 6 Fixme SHDocVw::WebBrowser* webBrowser = new SHDocVw::WebBrowser; QVERIFY(webBrowser); bool addressBar = webBrowser->AddressBar(); diff --git a/tools/dumpcpp/dumpcpp.pro b/tools/dumpcpp/dumpcpp.pro index aaaf5f8..5b56cea 100644 --- a/tools/dumpcpp/dumpcpp.pro +++ b/tools/dumpcpp/dumpcpp.pro @@ -1,7 +1,8 @@ QT += axcontainer widgets core-private DEFINES += QT_NO_CAST_TO_ASCII QT_ASCII_CAST_WARNINGS -SOURCES = main.cpp +SOURCES = main.cpp moc.cpp +HEADERS += moc.h QMAKE_TARGET_DESCRIPTION = "Active Qt DumpCpp" load(qt_tool) diff --git a/tools/dumpcpp/main.cpp b/tools/dumpcpp/main.cpp index 1545a39..d50c65d 100644 --- a/tools/dumpcpp/main.cpp +++ b/tools/dumpcpp/main.cpp @@ -26,10 +26,13 @@ ** ****************************************************************************/ +#include "moc.h" + #include <QAxObject> #include <QFile> #include <QMetaObject> #include <QMetaEnum> +#include <QDebug> #include <QTextStream> #include <QSettings> #include <QStringList> @@ -85,17 +88,8 @@ void writeEnums(QTextStream &out, const QMetaObject *mo) { // enums for (int ienum = mo->enumeratorOffset(); ienum < mo->enumeratorCount(); ++ienum) { - QMetaEnum metaEnum = mo->enumerator(ienum); - out << " enum " << metaEnum.name() << " {" << Qt::endl; - for (int k = 0; k < metaEnum.keyCount(); ++k) { - QByteArray key(metaEnum.key(k)); - out << " " << key.leftJustified(24) << "= " << metaEnum.value(k); - if (k < metaEnum.keyCount() - 1) - out << ','; - out << Qt::endl; - } - out << " };" << Qt::endl; - out << Qt::endl; + formatCppEnum(out, mo->enumerator(ienum)); + out << '\n'; } } @@ -184,9 +178,12 @@ static void formatConstructorSignature(QTextStream &out, ObjectCategories catego out << ')'; } -static void formatConstructorBody(QTextStream &out, const QByteArray &className, +static void formatConstructorBody(QTextStream &out, const QByteArray &nameSpace, + const QByteArray &className, const QString &controlID, ObjectCategories category) { + if (!nameSpace.isEmpty()) + out << nameSpace << "::"; out << className << "::" << className; formatConstructorSignature(out, category, false); out << " :" << Qt::endl << " "; @@ -258,21 +255,8 @@ void generateClassDecl(QTextStream &out, const QMetaObject *mo, functions << className; // enums - if (nameSpace.isEmpty() && !(category & OnlyInlines)) { - for (int ienum = mo->enumeratorOffset(); ienum < mo->enumeratorCount(); ++ienum) { - QMetaEnum metaEnum = mo->enumerator(ienum); - out << " enum " << metaEnum.name() << " {" << Qt::endl; - for (int k = 0; k < metaEnum.keyCount(); ++k) { - QByteArray key(metaEnum.key(k)); - out << " " << key.leftJustified(24) << "= " << metaEnum.value(k); - if (k < metaEnum.keyCount() - 1) - out << ','; - out << Qt::endl; - } - out << " };" << Qt::endl; - out << Qt::endl; - } - } + if (nameSpace.isEmpty() && !(category & OnlyInlines)) + writeEnums(out, mo); // QAxBase public virtual functions. QByteArrayList axBase_vfuncs; axBase_vfuncs.append("metaObject"); @@ -366,13 +350,7 @@ void generateClassDecl(QTextStream &out, const QMetaObject *mo, functions << propertyName; if (property.isWritable()) { - QByteArray setter(propertyName); - if (isupper(setter.at(0))) { - setter = "Set" + setter; - } else { - setter[0] = char(toupper(setter[0])); - setter = "set" + setter; - } + const QByteArray setter = setterName(propertyName); out << indent << "inline " << "void "; if (category & OnlyInlines) @@ -558,168 +536,10 @@ void generateClassDecl(QTextStream &out, const QMetaObject *mo, } } -#define addStringIdx(string) \ - out << stridx(string) << ", "; - -// The following functions were copied from moc generator with only some minor changes -void strreg(const QByteArray &s) -{ - if (!stringIndex.contains(s)) { - stringIndex.insert(s, strings.size()); - strings.append(s); - } -} - -void strDetachAndRegister(QByteArray s) -{ - s.detach(); - strreg(s); -} - -int stridx(const QByteArray &s) -{ - int i = stringIndex.value(s); - Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); - return i; -} - -const char *metaTypeEnumValueString(int type) -{ -#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType) \ - case QMetaType::MetaTypeName: return #MetaTypeName; - - switch (type) { -QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) - } -#undef RETURN_METATYPENAME_STRING - return nullptr; -} - -int nameToBuiltinType(const QByteArray &name) -{ - if (name.isEmpty()) - return 0; - - const int tp = QMetaType::type(name.constData()); - return tp < QMetaType::User ? tp : QMetaType::UnknownType; -} - -void copyFileToStream(QFile *file, QTextStream *stream) -{ - file->seek(0); - QByteArray buffer; - const int bufferSize = 4096 * 1024; - buffer.resize(bufferSize); - while (!file->atEnd()) { - const int bytesRead = static_cast<int>(file->read(buffer.data(), bufferSize)); - if (bytesRead < bufferSize) { - buffer.resize(bytesRead); - *stream << buffer; - buffer.resize(bufferSize); - } else { - *stream << buffer; - } - } -} - -void generateTypeInfo(QTextStream &out, const QByteArray &typeName) -{ - if (QtPrivate::isBuiltinType(typeName)) { - int type; - QByteArray valueString; - if (typeName == "qreal") { - type = QMetaType::UnknownType; - valueString = "QReal"; - } else { - type = nameToBuiltinType(typeName); - valueString = metaTypeEnumValueString(type); - } - if (!valueString.isEmpty()) { - out << "QMetaType::" << valueString; - } else { - Q_ASSERT(type != QMetaType::UnknownType); - out << type; - } - } else { - Q_ASSERT(!typeName.isEmpty()); - out << "0x80000000 | " << stridx(typeName); - } -} -// End functions copied from moc generator - -void generateMethods(QTextStream &out, const QMetaObject *mo, const QMetaMethod::MethodType funcType, int ¶msIndex) -{ - out << "// "; - MethodFlags funcTypeFlag; - if (funcType == QMetaMethod::Signal) { - out << "signal"; - funcTypeFlag = MethodSignal; - } else { - out << "slot"; - funcTypeFlag = MethodSlot; - } - out << ": name, argc, parameters, tag, flags" << Qt::endl; - - int methodCount = mo->methodCount(); - for (int i = mo->methodOffset(); i < methodCount; ++i) { - const QMetaMethod method(mo->method(i)); - if (method.methodType() != funcType) - continue; - out << " "; - addStringIdx(method.name()); - out << method.parameterCount() << ", "; - out << paramsIndex << ", "; - addStringIdx(method.tag()); - out << (AccessProtected | method.attributes() | funcTypeFlag) << ',' << Qt::endl; - paramsIndex += 1 + method.parameterCount() * 2; - } - out << Qt::endl; -} - -void generateMethodParameters(QTextStream &out, const QMetaObject *mo, const QMetaMethod::MethodType funcType) -{ - out << "// "; - if (funcType == QMetaMethod::Signal) - out << "signal"; - else if (funcType == QMetaMethod::Slot) - out << "slot"; - out << ": parameters" << Qt::endl; - - int methodCount = mo->methodCount(); - for (int i = mo->methodOffset(); i < methodCount; ++i) { - const QMetaMethod method(mo->method(i)); - if (method.methodType() != funcType) - continue; - - out << " "; - - int argsCount = method.parameterCount(); - - // Return type - generateTypeInfo(out, method.typeName()); - out << ','; - - // Parameter types - const auto parameterTypes = method.parameterTypes(); - for (int j = 0; j < argsCount; ++j) { - out << ' '; - generateTypeInfo(out, parameterTypes.at(j)); - out << ','; - } - - // Parameter names - const auto parameterNames = method.parameterNames(); - for (int j = 0; j < argsCount; ++j) - out << ' ' << stridx(parameterNames.at(j)) << ','; - - out << Qt::endl; - } - out << Qt::endl; -} - -void generateClassImpl(QTextStream &out, const QMetaObject *mo, const QByteArray &className, +bool generateClassImpl(QTextStream &out, const QMetaObject *mo, const QByteArray &className, const QString &controlID, - const QByteArray &nameSpace, ObjectCategories category) + const QByteArray &nameSpace, ObjectCategories category, + QString *errorString) { Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 8, "dumpcpp should generate the same version as moc"); @@ -727,203 +547,20 @@ void generateClassImpl(QTextStream &out, const QMetaObject *mo, const QByteArray if (!nameSpace.isEmpty()) qualifiedClassName = nameSpace + "::"; qualifiedClassName += className; - QByteArray qualifiedClassNameIdentifier = qualifiedClassName; - qualifiedClassNameIdentifier.replace(':', '_'); - - int allClassInfoCount = mo->classInfoCount(); - int allMethodCount = mo->methodCount(); - int allPropertyCount = mo->propertyCount(); - int allEnumCount = mo->enumeratorCount(); - - int thisClassInfoCount = allClassInfoCount - mo->classInfoOffset(); - int thisEnumCount = allEnumCount - mo->enumeratorOffset(); - int thisMethodCount = allMethodCount - mo->methodOffset(); - int thisPropertyCount = allPropertyCount - mo->propertyOffset(); - - int signalCount = 0; - int slotCount = 0; - int combinedParameterCount = 0; - int enumStart = MetaObjectPrivateFieldCount; - - // Register strings - strreg(qualifiedClassName); - for (int i = mo->classInfoOffset(); i < allClassInfoCount; ++i) { - const QMetaClassInfo classInfo = mo->classInfo(i); - strreg(classInfo.name()); - strreg(classInfo.value()); - } - for (int i = mo->methodOffset(); i < allMethodCount; ++i) { - const QMetaMethod method(mo->method(i)); - if (method.methodType() == QMetaMethod::Signal) - signalCount++; - if (method.methodType() == QMetaMethod::Slot) - slotCount++; - int argsCount = method.parameterCount(); - combinedParameterCount += argsCount; - - strDetachAndRegister(method.name()); - QByteArray typeName = method.typeName(); - if (!QtPrivate::isBuiltinType(typeName)) - strreg(typeName); - strreg(method.tag()); - - const auto parameterNames = method.parameterNames(); - const auto parameterTypes = method.parameterTypes(); - for (int j = 0; j < argsCount; ++j) { - if (!QtPrivate::isBuiltinType(parameterTypes.at(j))) - strDetachAndRegister(parameterTypes.at(j)); - strDetachAndRegister(parameterNames.at(j)); - } - } - for (int i = mo->propertyOffset(); i < allPropertyCount; ++i) { - const QMetaProperty property = mo->property(i); - strreg(property.name()); - if (!QtPrivate::isBuiltinType(property.typeName())) - strreg(property.typeName()); - } - for (int i = mo->enumeratorOffset(); i < allEnumCount; ++i) { - const QMetaEnum enumerator = mo->enumerator(i); - strreg(enumerator.name()); - for (int j = 0; j < enumerator.keyCount(); ++j) - strreg(enumerator.key(j)); - } - - // Build data array - out << "static const uint qt_meta_data_" << qualifiedClassNameIdentifier << "[] = {" << Qt::endl; - out << Qt::endl; - out << " // content:" << Qt::endl; - out << " 7, // revision" << Qt::endl; - out << " "; - addStringIdx(qualifiedClassName); - out << " // classname" << Qt::endl; - out << " " << thisClassInfoCount << ", " << (thisClassInfoCount ? enumStart : 0) << ", // classinfo" << Qt::endl; - enumStart += thisClassInfoCount * 2; - out << " " << thisMethodCount << ", " << (thisMethodCount ? enumStart : 0) << ", // methods" << Qt::endl; - enumStart += thisMethodCount * 5; - int paramsIndex = enumStart; - enumStart += (combinedParameterCount * 2); // parameter types + names - enumStart += thisMethodCount; // return types - out << " " << thisPropertyCount << ", " << (thisPropertyCount ? enumStart : 0) << ", // properties" << Qt::endl; - enumStart += thisPropertyCount * 3; - out << " " << thisEnumCount << ", " << (thisEnumCount ? enumStart : 0) << ", // enums/sets" << Qt::endl; - out << " 0, 0, // constructors" << Qt::endl; - out << " 0, // flags" << Qt::endl; - out << " " << signalCount << ", // signal count" << Qt::endl; - out << Qt::endl; - - if (thisClassInfoCount) { - out << " // classinfo: key, value" << Qt::endl; - for (int i = mo->classInfoOffset(); i < allClassInfoCount; ++i) { - QMetaClassInfo classInfo = mo->classInfo(i); - out << " "; - addStringIdx(classInfo.name()); - addStringIdx(classInfo.value()); - out << Qt::endl; - } - out << Qt::endl; - } - - // Signal/Slot arrays - if (signalCount) - generateMethods(out, mo, QMetaMethod::Signal, paramsIndex); - if (slotCount) - generateMethods(out, mo, QMetaMethod::Slot, paramsIndex); - - // Method parameter arrays - if (signalCount) - generateMethodParameters(out, mo, QMetaMethod::Signal); - if (slotCount) - generateMethodParameters(out, mo, QMetaMethod::Slot); - - if (thisPropertyCount) { - out << " // properties: name, type, flags" << Qt::endl; - for (int i = mo->propertyOffset(); i < allPropertyCount; ++i) { - QMetaProperty property = mo->property(i); - out << " "; - addStringIdx(property.name()); - generateTypeInfo(out, property.typeName()); - out << ", "; - - uint flags = 0; - const auto vartype = property.type(); - if (vartype != QVariant::Invalid && vartype != QVariant::UserType) - flags = uint(vartype) << 24; - - if (property.isReadable()) - flags |= Readable; - if (property.isWritable()) - flags |= Writable; - if (property.isEnumType()) - flags |= EnumOrFlag; - if (property.isDesignable()) - flags |= Designable; - if (property.isScriptable()) - flags |= Scriptable; - if (property.isStored()) - flags |= Stored; - if (property.isEditable()) - flags |= Editable; - - out << "0x" << QString::number(flags, 16).rightJustified(8, QLatin1Char('0')) - << ", \t\t // " << property.typeName() << ' ' << property.name() - << Qt::endl; - } - out << Qt::endl; - } - - if (thisEnumCount) { - out << " // enums: name, flags, count, data" << Qt::endl; - enumStart += thisEnumCount * 4; - for (int i = mo->enumeratorOffset(); i < allEnumCount; ++i) { - QMetaEnum enumerator = mo->enumerator(i); - out << " "; - addStringIdx(enumerator.name()); - out << (enumerator.isFlag() ? "0x1" : "0x0") << ", " << enumerator.keyCount() << ", " << enumStart << ", " << Qt::endl; - enumStart += enumerator.keyCount() * 2; - } - out << Qt::endl; - out << " // enum data: key, value" << Qt::endl; - for (int i = mo->enumeratorOffset(); i < allEnumCount; ++i) { - QMetaEnum enumerator = mo->enumerator(i); - for (int j = 0; j < enumerator.keyCount(); ++j) { - out << " "; - addStringIdx(enumerator.key(j)); - out << "uint("; - if (nameSpace.isEmpty()) - out << className << "::"; - else - out << nameSpace << "::"; - out << enumerator.key(j) << ")," << Qt::endl; - } - } + const QString moCode = mocCode(mo, QLatin1String(qualifiedClassName), + category.testFlag(ActiveX) ? QLatin1String("QWidget") : QLatin1String("QObject"), + errorString); + if (moCode.isEmpty()) { + out << "#error moc error\n"; + return false; } - out << " 0 // eod" << Qt::endl; - out << "};" << Qt::endl; - out << Qt::endl; - formatConstructorBody(out, className, controlID, category); + out << moCode << "\n\n"; - out << "const QMetaObject " << className << "::staticMetaObject = {" << Qt::endl; - if (category & ActiveX) - out << "{ &QWidget::staticMetaObject," << Qt::endl; - else - out << "{ &QObject::staticMetaObject," << Qt::endl; - out << "qt_meta_stringdata_all.data," << Qt::endl; - out << "qt_meta_data_" << qualifiedClassNameIdentifier << ", nullptr, nullptr, nullptr }" << Qt::endl; - out << "};" << Qt::endl; - out << Qt::endl; + formatConstructorBody(out, nameSpace, className, controlID, category); - out << "void *" << className << "::qt_metacast(const char *_clname)" << Qt::endl; - out << '{' << Qt::endl; - out << " if (!_clname) return nullptr;" << Qt::endl; - out << " if (!strcmp(_clname, \"" << qualifiedClassName << "\"))" << Qt::endl; - out << " return static_cast<void*>(const_cast<" << className << "*>(this));" << Qt::endl; - if (category & ActiveX) - out << " return QAxWidget::qt_metacast(_clname);" << Qt::endl; - else - out << " return QAxObject::qt_metacast(_clname);" << Qt::endl; - out << '}' << Qt::endl; + return true; } static void formatCommentBlockFooter(const QString &typeLibFile, QTextStream &str) @@ -1020,12 +657,8 @@ bool generateTypeLibrary(QString typeLibFile, QString outname, QMetaObject *namespaceObject = qax_readEnumInfo(typelib, nullptr); - QTemporaryFile classImplFile; - if (!classImplFile.open()) { - qWarning("dumpcpp: Cannot open temporary file."); - return false; - } - QTextStream classImplOut(&classImplFile); + QString classImpl; + QTextStream classImplOut(&classImpl); QFile implFile(outname + QLatin1String(".cpp")); QTextStream implOut(&implFile); if (!(category & (NoMetaObject|NoImplementation))) { @@ -1043,8 +676,6 @@ bool generateTypeLibrary(QString typeLibFile, QString outname, implOut << "#include \"" << outname << ".h\"" << Qt::endl; implOut << "#include <OAIdl.h>" << Qt::endl; // For IDispatch implOut << Qt::endl; - implOut << "using namespace " << libName << ';' << Qt::endl; - implOut << Qt::endl; } QFile declFile(outname + QLatin1String(".h")); @@ -1129,7 +760,6 @@ bool generateTypeLibrary(QString typeLibFile, QString outname, default: break; } - qax_deleteMetaObject(metaObject); typeinfo->ReleaseTypeAttr(typeattr); typeinfo->Release(); @@ -1282,9 +912,14 @@ bool generateTypeLibrary(QString typeLibFile, QString outname, object_category | OnlyInlines); inlinesOut << Qt::endl; } - if (implFile.isOpen()) - generateClassImpl(classImplOut, metaObject, className, guid.toString(), libNameBa, - object_category); + if (implFile.isOpen()) { + QString errorString; + if (!generateClassImpl(classImplOut, metaObject, className, guid.toString(), libNameBa, + object_category, &errorString)) { + qWarning("%s", qPrintable(errorString)); + return false; + } + } } currentTypeInfo = nullptr; } @@ -1297,93 +932,9 @@ bool generateTypeLibrary(QString typeLibFile, QString outname, // String table generation logic was ported from moc generator, with some modifications // required to split large stringdata arrays. - if (!strings.isEmpty() && implFile.isOpen()) { - // - // Build stringdata struct - // - implOut << "struct qt_meta_stringdata_all_t {" << Qt::endl; - implOut << " uint data[" << strings.size() * 2 << "];" << Qt::endl; - - QVector<QByteArrayList> listVector; - QByteArrayList currentList; - - int currentTableLen = 0; - for (const auto &s : strings) { - currentTableLen += s.length() + 1; - currentList.append(s); - // Split strings into chunks less than 64k to work around compiler limits. - if (currentTableLen > 60000) { - implOut << " char stringdata" << listVector.size() << '[' << currentTableLen + 1 << "];" << Qt::endl; - listVector.append(currentList); - currentList.clear(); - currentTableLen = 0; - } - } - implOut << " char stringdata" << listVector.size() << '[' << currentTableLen + 1 << "];" << Qt::endl; - implOut << "};" << Qt::endl; - listVector.append(currentList); - - implOut << "#define QT_MOC_LITERAL(ofs, len, table) \\" << Qt::endl - << " uint(offsetof(qt_meta_stringdata_all_t, stringdata##table) + ofs), len," << Qt::endl; - - implOut << "static const qt_meta_stringdata_all_t qt_meta_stringdata_all = {" << Qt::endl; - implOut << " {" << Qt::endl; - - int totalStringCount = 0; - for (int i = 0; i < listVector.size(); ++i) { - int idx = 0; - for (int j = 0; j < listVector[i].size(); j++) { - if (totalStringCount) - implOut << ',' << Qt::endl; - const QByteArray &str = listVector[i].at(j); - implOut << "QT_MOC_LITERAL(" << idx << ", " << str.length() << ", " << i << ')'; - idx += str.length() + 1; - } - } - implOut << Qt::endl << " }"; - - // - // Build stringdata arrays - // - for (const auto &l : listVector) { - int col = 0; - int len = 0; - implOut << ',' << Qt::endl; - implOut << " \""; - for (const auto &s : l) { - len = s.length(); - if (col && col + len >= 150) { - implOut << '"' << Qt::endl << " \""; - col = 0; - } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { - implOut << "\"\""; - len += 2; - } - int idx = 0; - while (idx < s.length()) { - if (idx > 0) { - col = 0; - implOut << '"' << Qt::endl << " \""; - } - int spanLen = qMin(150, s.length() - idx); - implOut << s.mid(idx, spanLen); - idx += spanLen; - col += spanLen; - } - - implOut << "\\0"; - col += len + 2; - } - implOut << '"'; - } - // Terminate stringdata struct - implOut << Qt::endl << "};" << Qt::endl; - - implOut << "#undef QT_MOC_LITERAL" << Qt::endl << Qt::endl; - + if (implFile.isOpen()) { classImplOut.flush(); - copyFileToStream(&classImplFile, &implOut); - implOut << Qt::endl; + implOut << classImpl << Qt::endl; } qax_deleteMetaObject(namespaceObject); 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 diff --git a/tools/dumpcpp/moc.h b/tools/dumpcpp/moc.h new file mode 100644 index 0000000..30a5030 --- /dev/null +++ b/tools/dumpcpp/moc.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef __MOC__ +#define __MOC__ + +#include <QString> + +QT_FORWARD_DECLARE_CLASS(QMetaEnum); +QT_FORWARD_DECLARE_CLASS(QTextStream); + +QT_BEGIN_NAMESPACE + +QByteArray setterName(const QByteArray &propertyName); + +void formatCppEnum(QTextStream &str, const QMetaEnum &metaEnum); + +QString mocCode(const QMetaObject *, const QString &qualifiedClassName, + QString baseClass, QString *errorString); + +QT_END_NAMESPACE + +#endif // __MOC__ |