1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <QtDeclarative/QtDeclarative>
43 #include <QtDeclarative/private/qdeclarativemetatype_p.h>
44 #include <QtDeclarative/private/qdeclarativeopenmetaobject_p.h>
45 #include <QtDeclarative/QDeclarativeView>
46 
47 #include <QtGui/QApplication>
48 
49 #include <QtCore/QSet>
50 #include <QtCore/QMetaObject>
51 #include <QtCore/QMetaProperty>
52 #include <QtCore/QDebug>
53 #include <QtCore/private/qobject_p.h>
54 #include <QtCore/private/qmetaobject_p.h>
55 
56 #include <iostream>
57 
58 #include "qmlstreamwriter.h"
59 
60 #ifdef QT_SIMULATOR
61 #include <QtGui/private/qsimulatorconnection_p.h>
62 #endif
63 
64 #ifdef Q_OS_UNIX
65 #include <signal.h>
66 #endif
67 #if defined(Q_OS_WIN)
68 #  if !defined(Q_CC_MINGW)
69 #    include <crtdbg.h>
70 #  endif
71 #include <qt_windows.h>
72 #endif
73 
74 QString pluginImportPath;
75 bool verbose = false;
76 bool creatable = true;
77 
78 QString currentProperty;
79 QString inObjectInstantiation;
80 
collectReachableMetaObjects(const QMetaObject * meta,QSet<const QMetaObject * > * metas)81 void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas)
82 {
83     if (! meta || metas->contains(meta))
84         return;
85 
86     // dynamic meta objects break things badly, so just ignore them
87     const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
88     if (!(mop->flags & DynamicMetaObject))
89         metas->insert(meta);
90 
91     collectReachableMetaObjects(meta->superClass(), metas);
92 }
93 
collectReachableMetaObjects(QObject * object,QSet<const QMetaObject * > * metas)94 void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
95 {
96     if (! object)
97         return;
98 
99     const QMetaObject *meta = object->metaObject();
100     if (verbose)
101         qDebug() << "Processing object" << meta->className();
102     collectReachableMetaObjects(meta, metas);
103 
104     for (int index = 0; index < meta->propertyCount(); ++index) {
105         QMetaProperty prop = meta->property(index);
106         if (QDeclarativeMetaType::isQObject(prop.userType())) {
107             if (verbose)
108                 qDebug() << "  Processing property" << prop.name();
109             currentProperty = QString("%1::%2").arg(meta->className(), prop.name());
110 
111             // if the property was not initialized during construction,
112             // accessing a member of oo is going to cause a segmentation fault
113             QObject *oo = QDeclarativeMetaType::toQObject(prop.read(object));
114             if (oo && !metas->contains(oo->metaObject()))
115                 collectReachableMetaObjects(oo, metas);
116             currentProperty.clear();
117         }
118     }
119 }
120 
collectReachableMetaObjects(const QDeclarativeType * ty,QSet<const QMetaObject * > * metas)121 void collectReachableMetaObjects(const QDeclarativeType *ty, QSet<const QMetaObject *> *metas)
122 {
123     collectReachableMetaObjects(ty->metaObject(), metas);
124     if (ty->attachedPropertiesType())
125         collectReachableMetaObjects(ty->attachedPropertiesType(), metas);
126 }
127 
128 /* We want to add the MetaObject for 'Qt' to the list, this is a
129    simple way to access it.
130 */
131 class FriendlyQObject: public QObject
132 {
133 public:
qtMeta()134     static const QMetaObject *qtMeta() { return &staticQtMetaObject; }
135 };
136 
137 /* When we dump a QMetaObject, we want to list all the types it is exported as.
138    To do this, we need to find the QDeclarativeTypes associated with this
139    QMetaObject.
140 */
141 static QHash<QByteArray, QSet<const QDeclarativeType *> > qmlTypesByCppName;
142 
143 static QHash<QByteArray, QByteArray> cppToId;
144 
145 /* Takes a C++ type name, such as Qt::LayoutDirection or QString and
146    maps it to how it should appear in the description file.
147 
148    These names need to be unique globally, so we don't change the C++ symbol's
149    name much. It is mostly used to for explicit translations such as
150    QString->string and translations for extended QML objects.
151 */
convertToId(const QByteArray & cppName)152 QByteArray convertToId(const QByteArray &cppName)
153 {
154     return cppToId.value(cppName, cppName);
155 }
156 
convertToId(const QMetaObject * mo)157 QByteArray convertToId(const QMetaObject *mo)
158 {
159     QByteArray className(mo->className());
160     if (!className.isEmpty())
161         return convertToId(className);
162 
163     // likely a metaobject generated for an extended qml object
164     if (mo->superClass()) {
165         className = convertToId(mo->superClass());
166         className.append("_extended");
167         return className;
168     }
169 
170     static QHash<const QMetaObject *, QByteArray> generatedNames;
171     className = generatedNames.value(mo);
172     if (!className.isEmpty())
173         return className;
174 
175     qWarning() << "Found a QMetaObject without a className, generating a random name";
176     className = QByteArray("error-unknown-name-");
177     className.append(QByteArray::number(generatedNames.size()));
178     generatedNames.insert(mo, className);
179     return className;
180 }
181 
collectReachableMetaObjects(const QList<QDeclarativeType * > & skip=QList<QDeclarativeType * > ())182 QSet<const QMetaObject *> collectReachableMetaObjects(const QList<QDeclarativeType *> &skip = QList<QDeclarativeType *>())
183 {
184     QSet<const QMetaObject *> metas;
185     metas.insert(FriendlyQObject::qtMeta());
186 
187     QHash<QByteArray, QSet<QByteArray> > extensions;
188     foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
189         qmlTypesByCppName[ty->metaObject()->className()].insert(ty);
190         if (ty->isExtendedType()) {
191             extensions[ty->typeName()].insert(ty->metaObject()->className());
192         }
193         collectReachableMetaObjects(ty, &metas);
194     }
195 
196     // Adjust exports of the base object if there are extensions.
197     // For each export of a base object there can be a single extension object overriding it.
198     // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export
199     //          of QGraphicsWidget.
200     foreach (const QByteArray &baseCpp, extensions.keys()) {
201         QSet<const QDeclarativeType *> baseExports = qmlTypesByCppName.value(baseCpp);
202 
203         const QSet<QByteArray> extensionCppNames = extensions.value(baseCpp);
204         foreach (const QByteArray &extensionCppName, extensionCppNames) {
205             const QSet<const QDeclarativeType *> extensionExports = qmlTypesByCppName.value(extensionCppName);
206 
207             // remove extension exports from base imports
208             // unfortunately the QDeclarativeType pointers don't match, so can't use QSet::substract
209             QSet<const QDeclarativeType *> newBaseExports;
210             foreach (const QDeclarativeType *baseExport, baseExports) {
211                 bool match = false;
212                 foreach (const QDeclarativeType *extensionExport, extensionExports) {
213                     if (baseExport->qmlTypeName() == extensionExport->qmlTypeName()
214                             && baseExport->majorVersion() == extensionExport->majorVersion()
215                             && baseExport->minorVersion() == extensionExport->minorVersion()) {
216                         match = true;
217                         break;
218                     }
219                 }
220                 if (!match)
221                     newBaseExports.insert(baseExport);
222             }
223             baseExports = newBaseExports;
224         }
225         qmlTypesByCppName[baseCpp] = baseExports;
226     }
227 
228     if (creatable) {
229         // find even more QMetaObjects by instantiating QML types and running
230         // over the instances
231         foreach (QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
232             if (skip.contains(ty))
233                 continue;
234             if (ty->isExtendedType())
235                 continue;
236             if (!ty->isCreatable())
237                 continue;
238             if (ty->typeName() == "QDeclarativeComponent")
239                 continue;
240 
241             QByteArray tyName = ty->qmlTypeName();
242             tyName = tyName.mid(tyName.lastIndexOf('/') + 1);
243             if (tyName.isEmpty())
244                 continue;
245 
246             inObjectInstantiation = tyName;
247             QObject *object = ty->create();
248             inObjectInstantiation.clear();
249 
250             if (object)
251                 collectReachableMetaObjects(object, &metas);
252             else
253                 qWarning() << "Could not create" << tyName;
254         }
255     }
256 
257     return metas;
258 }
259 
260 
261 class Dumper
262 {
263     QmlStreamWriter *qml;
264     QString relocatableModuleUri;
265 
266 public:
Dumper(QmlStreamWriter * qml)267     Dumper(QmlStreamWriter *qml) : qml(qml) {}
268 
setRelocatableModuleUri(const QString & uri)269     void setRelocatableModuleUri(const QString &uri)
270     {
271         relocatableModuleUri = uri;
272     }
273 
dump(const QMetaObject * meta)274     void dump(const QMetaObject *meta)
275     {
276         qml->writeStartObject("Component");
277 
278         QByteArray id = convertToId(meta);
279         qml->writeScriptBinding(QLatin1String("name"), enquote(id));
280 
281         for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
282             QMetaClassInfo classInfo = meta->classInfo(index);
283             if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
284                 qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
285                 break;
286             }
287         }
288 
289         if (meta->superClass())
290             qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
291 
292         QSet<const QDeclarativeType *> qmlTypes = qmlTypesByCppName.value(meta->className());
293         if (!qmlTypes.isEmpty()) {
294             QHash<QString, const QDeclarativeType *> exports;
295 
296             foreach (const QDeclarativeType *qmlTy, qmlTypes) {
297                 QString qmlTyName = qmlTy->qmlTypeName();
298                 // some qmltype names are missing the actual names, ignore that import
299                 if (qmlTyName.endsWith('/'))
300                     continue;
301                 if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
302                     qmlTyName.remove(0, relocatableModuleUri.size() + 1);
303                 }
304                 if (qmlTyName.startsWith("./")) {
305                     qmlTyName.remove(0, 2);
306                 }
307                 const QString exportString = enquote(
308                             QString("%1 %2.%3").arg(
309                                 qmlTyName,
310                                 QString::number(qmlTy->majorVersion()),
311                                 QString::number(qmlTy->minorVersion())));
312                 exports.insert(exportString, qmlTy);
313             }
314 
315             // ensure exports are sorted and don't change order when the plugin is dumped again
316             QStringList exportStrings = exports.keys();
317             qSort(exportStrings);
318             qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
319 
320             // write meta object revisions
321             QStringList metaObjectRevisions;
322             foreach (const QString &exportString, exportStrings) {
323                 int metaObjectRevision = exports[exportString]->metaObjectRevision();
324                 metaObjectRevisions += QString::number(metaObjectRevision);
325             }
326             qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
327 
328             if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
329                 // Can happen when a type is registered that returns itself as attachedPropertiesType()
330                 // because there is no creatable type to attach to.
331                 if (attachedType != meta) {
332                     qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
333                                                 convertToId(attachedType)));
334                 }
335             }
336         }
337 
338         for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
339             dump(meta->enumerator(index));
340 
341         for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index)
342             dump(meta->property(index));
343 
344         for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
345             dump(meta->method(index));
346 
347         qml->writeEndObject();
348     }
349 
writeEasingCurve()350     void writeEasingCurve()
351     {
352         qml->writeStartObject("Component");
353         qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve")));
354         qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QDeclarativeEasingValueType")));
355         qml->writeEndObject();
356     }
357 
358 private:
enquote(const QString & string)359     static QString enquote(const QString &string)
360     {
361         return QString("\"%1\"").arg(string);
362     }
363 
364     /* Removes pointer and list annotations from a type name, returning
365        what was removed in isList and isPointer
366     */
removePointerAndList(QByteArray * typeName,bool * isList,bool * isPointer)367     static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer)
368     {
369         static QByteArray declListPrefix = "QDeclarativeListProperty<";
370 
371         if (typeName->endsWith('*')) {
372             *isPointer = true;
373             typeName->truncate(typeName->length() - 1);
374             removePointerAndList(typeName, isList, isPointer);
375         } else if (typeName->startsWith(declListPrefix)) {
376             *isList = true;
377             typeName->truncate(typeName->length() - 1); // get rid of the suffix '>'
378             *typeName = typeName->mid(declListPrefix.size());
379             removePointerAndList(typeName, isList, isPointer);
380         }
381 
382         *typeName = convertToId(*typeName);
383     }
384 
writeTypeProperties(QByteArray typeName,bool isWritable)385     void writeTypeProperties(QByteArray typeName, bool isWritable)
386     {
387         bool isList = false, isPointer = false;
388         removePointerAndList(&typeName, &isList, &isPointer);
389 
390         qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
391         if (isList)
392             qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true"));
393         if (!isWritable)
394             qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
395         if (isPointer)
396             qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true"));
397     }
398 
dump(const QMetaProperty & prop)399     void dump(const QMetaProperty &prop)
400     {
401         qml->writeStartObject("Property");
402 
403         qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
404 #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
405         if (int revision = prop.revision())
406             qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
407 #endif
408         writeTypeProperties(prop.typeName(), prop.isWritable());
409 
410         qml->writeEndObject();
411     }
412 
dump(const QMetaMethod & meth)413     void dump(const QMetaMethod &meth)
414     {
415         if (meth.methodType() == QMetaMethod::Signal) {
416             if (meth.access() != QMetaMethod::Protected)
417                 return; // nothing to do.
418         } else if (meth.access() != QMetaMethod::Public) {
419             return; // nothing to do.
420         }
421 
422         QByteArray name = meth.signature();
423         int lparenIndex = name.indexOf('(');
424         if (lparenIndex == -1) {
425             return; // invalid signature
426         }
427         name = name.left(lparenIndex);
428 
429         if (meth.methodType() == QMetaMethod::Signal)
430             qml->writeStartObject(QLatin1String("Signal"));
431         else
432             qml->writeStartObject(QLatin1String("Method"));
433 
434         qml->writeScriptBinding(QLatin1String("name"), enquote(name));
435 
436 #if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
437         if (int revision = meth.revision())
438             qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
439 #endif
440 
441         const QString typeName = convertToId(meth.typeName());
442         if (! typeName.isEmpty())
443             qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
444 
445         for (int i = 0; i < meth.parameterTypes().size(); ++i) {
446             QByteArray argName = meth.parameterNames().at(i);
447 
448             qml->writeStartObject(QLatin1String("Parameter"));
449             if (! argName.isEmpty())
450                 qml->writeScriptBinding(QLatin1String("name"), enquote(argName));
451             writeTypeProperties(meth.parameterTypes().at(i), true);
452             qml->writeEndObject();
453         }
454 
455         qml->writeEndObject();
456     }
457 
dump(const QMetaEnum & e)458     void dump(const QMetaEnum &e)
459     {
460         qml->writeStartObject(QLatin1String("Enum"));
461         qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));
462 
463         QList<QPair<QString, QString> > namesValues;
464         for (int index = 0; index < e.keyCount(); ++index) {
465             namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
466         }
467 
468         qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues);
469         qml->writeEndObject();
470     }
471 };
472 
473 
474 enum ExitCode {
475     EXIT_INVALIDARGUMENTS = 1,
476     EXIT_SEGV = 2,
477     EXIT_IMPORTERROR = 3
478 };
479 
480 #ifdef Q_OS_UNIX
sigSegvHandler(int)481 void sigSegvHandler(int) {
482     fprintf(stderr, "Error: SEGV\n");
483     if (!currentProperty.isEmpty())
484         fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData());
485     if (!inObjectInstantiation.isEmpty())
486         fprintf(stderr, "While instantiating the object '%s'.\n", inObjectInstantiation.toLatin1().constData());
487     exit(EXIT_SEGV);
488 }
489 #endif
490 
printUsage(const QString & appName)491 void printUsage(const QString &appName)
492 {
493     qWarning() << qPrintable(QString(
494                                  "Usage: %1 [-v] [-noinstantiate] [-[non]relocatable] module.uri version [module/import/path]\n"
495                                  "       %1 [-v] [-noinstantiate] -path path/to/qmldir/directory [version]\n"
496                                  "       %1 [-v] -builtins\n"
497                                  "Example: %1 Qt.labs.particles 4.7 /home/user/dev/qt-install/imports").arg(
498                                  appName));
499 }
500 
main(int argc,char * argv[])501 int main(int argc, char *argv[])
502 {
503 #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW)
504     // we do not want windows popping up if the module loaded triggers an assert
505     SetErrorMode(SEM_NOGPFAULTERRORBOX);
506     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
507     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
508     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
509 #endif
510 #ifdef Q_OS_UNIX
511     // qmldump may crash, but we don't want any crash handlers to pop up
512     // therefore we intercept the segfault and just exit() ourselves
513     struct sigaction sigAction;
514 
515     sigemptyset(&sigAction.sa_mask);
516     sigAction.sa_handler = &sigSegvHandler;
517     sigAction.sa_flags   = 0;
518 
519     sigaction(SIGSEGV, &sigAction, 0);
520 #endif
521 
522 #ifdef QT_SIMULATOR
523     // Running this application would bring up the Qt Simulator (since it links QtGui), avoid that!
524     QtSimulatorPrivate::SimulatorConnection::createStubInstance();
525 #endif
526     QApplication app(argc, argv);
527     const QStringList args = app.arguments();
528     const QString appName = QFileInfo(app.applicationFilePath()).baseName();
529     if (args.size() < 2) {
530         printUsage(appName);
531         return EXIT_INVALIDARGUMENTS;
532     }
533 
534     QString pluginImportUri;
535     QString pluginImportVersion;
536     bool relocatable = true;
537     enum Action { Uri, Path, Builtins };
538     Action action = Uri;
539     {
540         QStringList positionalArgs;
541         foreach (const QString &arg, args) {
542             if (!arg.startsWith(QLatin1Char('-'))) {
543                 positionalArgs.append(arg);
544                 continue;
545             }
546 
547             if (arg == QLatin1String("--notrelocatable")
548                     || arg == QLatin1String("-notrelocatable")
549                     || arg == QLatin1String("--nonrelocatable")
550                     || arg == QLatin1String("-nonrelocatable")) {
551                 relocatable = false;
552             } else if (arg == QLatin1String("--relocatable")
553                         || arg == QLatin1String("-relocatable")) {
554                 relocatable = true;
555             } else if (arg == QLatin1String("--path")
556                        || arg == QLatin1String("-path")) {
557                 action = Path;
558             } else if (arg == QLatin1String("--builtins")
559                        || arg == QLatin1String("-builtins")) {
560                 action = Builtins;
561             } else if (arg == QLatin1String("-v")) {
562                 verbose = true;
563             } else if (arg == QLatin1String("--noinstantiate")
564                        || arg == QLatin1String("-noinstantiate")) {
565                 creatable = false;
566             } else {
567                 qWarning() << "Invalid argument: " << arg;
568                 return EXIT_INVALIDARGUMENTS;
569             }
570         }
571 
572         if (action == Uri) {
573             if (positionalArgs.size() != 3 && positionalArgs.size() != 4) {
574                 qWarning() << "Incorrect number of positional arguments";
575                 return EXIT_INVALIDARGUMENTS;
576             }
577             pluginImportUri = positionalArgs[1];
578             pluginImportVersion = positionalArgs[2];
579             if (positionalArgs.size() >= 4)
580                 pluginImportPath = positionalArgs[3];
581         } else if (action == Path) {
582             if (positionalArgs.size() != 2 && positionalArgs.size() != 3) {
583                 qWarning() << "Incorrect number of positional arguments";
584                 return EXIT_INVALIDARGUMENTS;
585             }
586             pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]);
587             if (positionalArgs.size() == 3)
588                 pluginImportVersion = positionalArgs[2];
589         } else if (action == Builtins) {
590             if (positionalArgs.size() != 1) {
591                 qWarning() << "Incorrect number of positional arguments";
592                 return EXIT_INVALIDARGUMENTS;
593             }
594         }
595     }
596 
597     QDeclarativeView view;
598     QDeclarativeEngine *engine = view.engine();
599     if (!pluginImportPath.isEmpty()) {
600         QDir cur = QDir::current();
601         cur.cd(pluginImportPath);
602         pluginImportPath = cur.absolutePath();
603         QDir::setCurrent(pluginImportPath);
604         engine->addImportPath(pluginImportPath);
605     }
606 
607     // find all QMetaObjects reachable from the builtin module
608     QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects();
609     QList<QDeclarativeType *> defaultTypes = QDeclarativeMetaType::qmlTypes();
610 
611     // this will hold the meta objects we want to dump information of
612     QSet<const QMetaObject *> metas;
613 
614     if (action == Builtins) {
615         metas = defaultReachable;
616     } else {
617         // find a valid QtQuick import
618         QByteArray importCode;
619         QDeclarativeType *qtObjectType = QDeclarativeMetaType::qmlType(&QObject::staticMetaObject);
620         if (!qtObjectType) {
621             qWarning() << "Could not find QtObject type";
622             importCode = QByteArray("import QtQuick 1.0\n");
623         } else {
624             QByteArray module = qtObjectType->qmlTypeName();
625             module = module.mid(0, module.lastIndexOf('/'));
626             importCode = QString("import %1 %2.%3\n").arg(module,
627                                                           QString::number(qtObjectType->majorVersion()),
628                                                           QString::number(qtObjectType->minorVersion())).toUtf8();
629         }
630 
631         // find all QMetaObjects reachable when the specified module is imported
632         if (action != Path) {
633             importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toAscii();
634         } else {
635             // pluginImportVersion can be empty
636             importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toAscii();
637         }
638 
639         // create a component with these imports to make sure the imports are valid
640         // and to populate the declarative meta type system
641         {
642             QByteArray code = importCode;
643             code += "QtObject {}";
644             QDeclarativeComponent c(engine);
645 
646             c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml"));
647             c.create();
648             if (!c.errors().isEmpty()) {
649                 foreach (const QDeclarativeError &error, c.errors())
650                     qWarning() << error.toString();
651                 return EXIT_IMPORTERROR;
652             }
653         }
654 
655         QSet<const QMetaObject *> candidates = collectReachableMetaObjects(defaultTypes);
656         candidates.subtract(defaultReachable);
657 
658         // Also eliminate meta objects with the same classname.
659         // This is required because extended objects seem not to share
660         // a single meta object instance.
661         QSet<QByteArray> defaultReachableNames;
662         foreach (const QMetaObject *mo, defaultReachable)
663             defaultReachableNames.insert(QByteArray(mo->className()));
664         foreach (const QMetaObject *mo, candidates) {
665             if (!defaultReachableNames.contains(mo->className()))
666                 metas.insert(mo);
667         }
668     }
669 
670     // setup static rewrites of type names
671     cppToId.insert("QString", "string");
672     cppToId.insert("QDeclarativeEasingValueType::Type", "Type");
673 
674     // start dumping data
675     QByteArray bytes;
676     QmlStreamWriter qml(&bytes);
677 
678     qml.writeStartDocument();
679     qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 1);
680     qml.write("\n"
681               "// This file describes the plugin-supplied types contained in the library.\n"
682               "// It is used for QML tooling purposes only.\n"
683               "\n");
684     qml.writeStartObject("Module");
685 
686     // put the metaobjects into a map so they are always dumped in the same order
687     QMap<QString, const QMetaObject *> nameToMeta;
688     foreach (const QMetaObject *meta, metas)
689         nameToMeta.insert(convertToId(meta), meta);
690 
691     Dumper dumper(&qml);
692     if (relocatable)
693         dumper.setRelocatableModuleUri(pluginImportUri);
694     foreach (const QMetaObject *meta, nameToMeta) {
695         dumper.dump(meta);
696     }
697 
698     // define QEasingCurve as an extension of QDeclarativeEasingValueType, this way
699     // properties using the QEasingCurve type get useful type information.
700     if (pluginImportUri.isEmpty())
701         dumper.writeEasingCurve();
702 
703     qml.writeEndObject();
704     qml.writeEndDocument();
705 
706     std::cout << bytes.constData() << std::flush;
707 
708     // workaround to avoid crashes on exit
709     QTimer timer;
710     timer.setSingleShot(true);
711     timer.setInterval(0);
712     QObject::connect(&timer, SIGNAL(timeout()), &app, SLOT(quit()));
713     timer.start();
714 
715     return app.exec();
716 }
717