summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-04-01 14:11:00 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-04-08 11:52:02 +0200
commitb4d965231e4aa22b625997e8d4271f3c6f872696 (patch)
tree6567c6be580a8c2129f3455fa14756128d8e92c6
parent37d19e4b5eda47c145eeeac884d0b558acddb19d (diff)
dumpcpp: Use moc to generate metaobject code
dumpcpp contained an old version of moc code, which is now out of date. Write out the interface as C++ code and run the real moc on it to generate this. Some modifications are required. Remove some asserts that checked on an empty meta object created in the fallback path of QAxBasePrivate::metaObject() which was removed by 0bcdc74f55574e1a45bef8728bd5093cf1acfc33. Task-number: QTBUG-82945 Change-Id: Ide58bae1440331ea4d5da0fcc74b41f49f09599a Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r--src/activeqt/container/qaxbase.cpp3
-rw-r--r--tests/auto/dumpcpp/tst_dumpcpp.cpp1
-rw-r--r--tools/dumpcpp/dumpcpp.pro3
-rw-r--r--tools/dumpcpp/main.cpp521
-rw-r--r--tools/dumpcpp/moc.cpp300
-rw-r--r--tools/dumpcpp/moc.h48
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 &paramsIndex)
-{
- 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__