qmlplugindump: Add support for composite types

Task-number: QTBUG-33106

Change-Id: I80fc817eb59256e860f3fdd591104930688ef84c
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Caroline Chao 2013-09-03 14:03:21 +02:00 committed by The Qt Project
parent 32a69d67af
commit 0ef673efe8
1 changed files with 150 additions and 65 deletions

View File

@ -140,6 +140,9 @@ public:
*/
static QHash<QByteArray, QSet<const QQmlType *> > qmlTypesByCppName;
// No different versioning possible for a composite type.
static QMap<QString, const QQmlType * > qmlTypesByCompositeName;
static QHash<QByteArray, QByteArray> cppToId;
/* Takes a C++ type name, such as Qt::LayoutDirection or QString and
@ -191,8 +194,9 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, const
if (ty->isExtendedType())
extensions[ty->typeName()].insert(ty->metaObject()->className());
collectReachableMetaObjects(ty, &metas);
} else {
qmlTypesByCompositeName[ty->elementName()] = ty;
}
// TODO actually handle composite types
}
// Adjust exports of the base object if there are extensions.
@ -288,73 +292,27 @@ public:
relocatableModuleUri = uri;
}
void dump(const QMetaObject *meta)
const QString getExportString(QString qmlTyName, int majorVersion, int minorVersion)
{
qml->writeStartObject("Component");
QByteArray id = convertToId(meta);
qml->writeScriptBinding(QLatin1String("name"), enquote(id));
for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
QMetaClassInfo classInfo = meta->classInfo(index);
if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
break;
}
if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
qmlTyName.remove(0, relocatableModuleUri.size() + 1);
}
if (meta->superClass())
qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
QSet<const QQmlType *> qmlTypes = qmlTypesByCppName.value(meta->className());
if (!qmlTypes.isEmpty()) {
QHash<QString, const QQmlType *> exports;
foreach (const QQmlType *qmlTy, qmlTypes) {
QString qmlTyName = qmlTy->qmlTypeName();
if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
qmlTyName.remove(0, relocatableModuleUri.size() + 1);
}
if (qmlTyName.startsWith("./")) {
qmlTyName.remove(0, 2);
}
if (qmlTyName.startsWith("/")) {
qmlTyName.remove(0, 1);
}
const QString exportString = enquote(
QString("%1 %2.%3").arg(
qmlTyName,
QString::number(qmlTy->majorVersion()),
QString::number(qmlTy->minorVersion())));
exports.insert(exportString, qmlTy);
}
// ensure exports are sorted and don't change order when the plugin is dumped again
QStringList exportStrings = exports.keys();
std::sort(exportStrings.begin(), exportStrings.end());
qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
// write meta object revisions
QStringList metaObjectRevisions;
foreach (const QString &exportString, exportStrings) {
int metaObjectRevision = exports[exportString]->metaObjectRevision();
metaObjectRevisions += QString::number(metaObjectRevision);
}
qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
// Can happen when a type is registered that returns itself as attachedPropertiesType()
// because there is no creatable type to attach to.
if (attachedType != meta) {
qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
convertToId(attachedType)));
}
}
if (qmlTyName.startsWith("./")) {
qmlTyName.remove(0, 2);
}
if (qmlTyName.startsWith("/")) {
qmlTyName.remove(0, 1);
}
const QString exportString = enquote(
QString("%1 %2.%3").arg(
qmlTyName,
QString::number(majorVersion),
QString::number(minorVersion)));
return exportString;
}
for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
dump(meta->enumerator(index));
void writeMetaContent(const QMetaObject *meta)
{
QSet<QString> implicitSignals;
for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
const QMetaProperty &property = meta->property(index);
@ -392,6 +350,131 @@ public:
for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
dump(meta->method(index), implicitSignals);
}
}
QString getPrototypeNameForCompositeType(const QMetaObject *metaObject)
{
QString prototypeName;
const QMetaObject *superMetaObject = metaObject->superClass();
if (!superMetaObject)
return "QObject";
QString className = convertToId(superMetaObject->className());
if (className.startsWith("QQuick"))
prototypeName = className;
else
prototypeName = getPrototypeNameForCompositeType(superMetaObject);
return prototypeName;
}
void dumpComposite(QQmlEngine *engine, const QQmlType *compositeType, QSet<QByteArray> &defaultReachableNames)
{
QQmlComponent e(engine, compositeType->sourceUrl());
QObject *object = e.create();
if (!object)
return;
qml->writeStartObject("Component");
const QMetaObject *mainMeta = object->metaObject();
// Get C++ base class name for the composite type
QString prototypeName = getPrototypeNameForCompositeType(mainMeta);
qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName));
QString qmlTyName = compositeType->qmlTypeName();
// name should be unique
qml->writeScriptBinding(QLatin1String("name"), enquote(qmlTyName));
const QString exportString = getExportString(qmlTyName, compositeType->majorVersion(), compositeType->minorVersion());
qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString);
qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList() << QString::number(compositeType->minorVersion()));
for (int index = mainMeta->classInfoCount() - 1 ; index >= 0 ; --index) {
QMetaClassInfo classInfo = mainMeta->classInfo(index);
if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
break;
}
}
QSet<const QMetaObject *> metas;
QSet<const QMetaObject *> candidatesComposite;
collectReachableMetaObjects(mainMeta, &candidatesComposite);
// Also eliminate meta objects with the same classname.
// This is required because extended objects seem not to share
// a single meta object instance.
foreach (const QMetaObject *mo, candidatesComposite) {
if (!defaultReachableNames.contains(mo->className()))
metas.insert(mo);
}
// put the metaobjects into a map so they are always dumped in the same order
QMap<QString, const QMetaObject *> nameToMeta;
foreach (const QMetaObject *meta, metas)
nameToMeta.insert(convertToId(meta), meta);
foreach (const QMetaObject *meta, nameToMeta)
writeMetaContent(meta);
qml->writeEndObject();
}
void dump(const QMetaObject *meta)
{
qml->writeStartObject("Component");
QByteArray id = convertToId(meta);
qml->writeScriptBinding(QLatin1String("name"), enquote(id));
for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
QMetaClassInfo classInfo = meta->classInfo(index);
if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
break;
}
}
if (meta->superClass())
qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
QSet<const QQmlType *> qmlTypes = qmlTypesByCppName.value(meta->className());
if (!qmlTypes.isEmpty()) {
QHash<QString, const QQmlType *> exports;
foreach (const QQmlType *qmlTy, qmlTypes) {
const QString exportString = getExportString(qmlTy->qmlTypeName(), qmlTy->majorVersion(), qmlTy->minorVersion());
exports.insert(exportString, qmlTy);
}
// ensure exports are sorted and don't change order when the plugin is dumped again
QStringList exportStrings = exports.keys();
std::sort(exportStrings.begin(), exportStrings.end());
qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
// write meta object revisions
QStringList metaObjectRevisions;
foreach (const QString &exportString, exportStrings) {
int metaObjectRevision = exports[exportString]->metaObjectRevision();
metaObjectRevisions += QString::number(metaObjectRevision);
}
qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
// Can happen when a type is registered that returns itself as attachedPropertiesType()
// because there is no creatable type to attach to.
if (attachedType != meta) {
qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
convertToId(attachedType)));
}
}
}
for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
dump(meta->enumerator(index));
writeMetaContent(meta);
qml->writeEndObject();
}
@ -673,6 +756,7 @@ int main(int argc, char *argv[])
// add some otherwise unreachable QMetaObjects
defaultReachable.insert(&QQuickMouseEvent::staticMetaObject);
// QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported
QSet<QByteArray> defaultReachableNames;
// this will hold the meta objects we want to dump information of
QSet<const QMetaObject *> metas;
@ -724,7 +808,6 @@ int main(int argc, char *argv[])
// Also eliminate meta objects with the same classname.
// This is required because extended objects seem not to share
// a single meta object instance.
QSet<QByteArray> defaultReachableNames;
foreach (const QMetaObject *mo, defaultReachable)
defaultReachableNames.insert(QByteArray(mo->className()));
foreach (const QMetaObject *mo, candidates) {
@ -762,6 +845,8 @@ int main(int argc, char *argv[])
foreach (const QMetaObject *meta, nameToMeta) {
dumper.dump(meta);
}
foreach (const QQmlType *compositeType, qmlTypesByCompositeName)
dumper.dumpComposite(&engine, compositeType, defaultReachableNames);
// define QEasingCurve as an extension of QQmlEasingValueType, this way
// properties using the QEasingCurve type get useful type information.