qmlplugindump: Take care of 'void' types
[profile/ivi/qtdeclarative.git] / tools / qmlplugindump / main.cpp
index c2d2681..b2e503c 100644 (file)
@@ -1,34 +1,34 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
 **
 ** This file is part of the tools applications of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
 ** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
-** rights.  These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
 **
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
 **
 **
 **
 **
 ****************************************************************************/
 
-#include <QtDeclarative/QtDeclarative>
-#include <QtDeclarative/private/qdeclarativemetatype_p.h>
-#include <QtDeclarative/private/qdeclarativeopenmetaobject_p.h>
-#include <QtDeclarative/QDeclarativeView>
-
-#include <QtGui/QApplication>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/private/qqmlmetatype_p.h>
+#include <QtQml/private/qqmlopenmetaobject_p.h>
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickpincharea_p.h>
 
+#include <QtGui/QGuiApplication>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
 #include <QtCore/QSet>
+#include <QtCore/QStringList>
+#include <QtCore/QTimer>
 #include <QtCore/QMetaObject>
 #include <QtCore/QMetaProperty>
 #include <QtCore/QDebug>
 #endif
 
 QString pluginImportPath;
+bool verbose = false;
+
+QString currentProperty;
+QString inObjectInstantiation;
 
-void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, bool extended = false)
 {
     if (! meta || metas->contains(meta))
         return;
 
-    // dynamic meta objects break things badly, so just ignore them
+    // dynamic meta objects can break things badly
+    // but extended types are usually fine
     const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
-    if (!(mop->flags & DynamicMetaObject))
+    if (extended || !(mop->flags & DynamicMetaObject))
         metas->insert(meta);
 
     collectReachableMetaObjects(meta->superClass(), metas);
 }
 
-QString currentProperty;
-
 void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
 {
     if (! object)
         return;
 
     const QMetaObject *meta = object->metaObject();
-    qDebug() << "Processing object" << meta->className();
+    if (verbose)
+        qDebug() << "Processing object" << meta->className();
     collectReachableMetaObjects(meta, metas);
 
     for (int index = 0; index < meta->propertyCount(); ++index) {
         QMetaProperty prop = meta->property(index);
-        if (QDeclarativeMetaType::isQObject(prop.userType())) {
-            qDebug() << "  Processing property" << prop.name();
+        if (QQmlMetaType::isQObject(prop.userType())) {
+            if (verbose)
+                qDebug() << "  Processing property" << prop.name();
             currentProperty = QString("%1::%2").arg(meta->className(), prop.name());
 
             // if the property was not initialized during construction,
             // accessing a member of oo is going to cause a segmentation fault
-            QObject *oo = QDeclarativeMetaType::toQObject(prop.read(object));
+            QObject *oo = QQmlMetaType::toQObject(prop.read(object));
             if (oo && !metas->contains(oo->metaObject()))
                 collectReachableMetaObjects(oo, metas);
             currentProperty.clear();
@@ -107,9 +116,9 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met
     }
 }
 
-void collectReachableMetaObjects(const QDeclarativeType *ty, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(const QQmlType *ty, QSet<const QMetaObject *> *metas)
 {
-    collectReachableMetaObjects(ty->metaObject(), metas);
+    collectReachableMetaObjects(ty->metaObject(), metas, ty->isExtendedType());
     if (ty->attachedPropertiesType())
         collectReachableMetaObjects(ty->attachedPropertiesType(), metas);
 }
@@ -124,10 +133,10 @@ public:
 };
 
 /* When we dump a QMetaObject, we want to list all the types it is exported as.
-   To do this, we need to find the QDeclarativeTypes associated with this
+   To do this, we need to find the QQmlTypes associated with this
    QMetaObject.
 */
-static QHash<QByteArray, QSet<const QDeclarativeType *> > qmlTypesByCppName;
+static QHash<QByteArray, QSet<const QQmlType *> > qmlTypesByCppName;
 
 static QHash<QByteArray, QByteArray> cppToId;
 
@@ -143,13 +152,38 @@ QByteArray convertToId(const QByteArray &cppName)
     return cppToId.value(cppName, cppName);
 }
 
-QSet<const QMetaObject *> collectReachableMetaObjects(const QString &importCode, QDeclarativeEngine *engine)
+QByteArray convertToId(const QMetaObject *mo)
+{
+    QByteArray className(mo->className());
+    if (!className.isEmpty())
+        return convertToId(className);
+
+    // likely a metaobject generated for an extended qml object
+    if (mo->superClass()) {
+        className = convertToId(mo->superClass());
+        className.append("_extended");
+        return className;
+    }
+
+    static QHash<const QMetaObject *, QByteArray> generatedNames;
+    className = generatedNames.value(mo);
+    if (!className.isEmpty())
+        return className;
+
+    qWarning() << "Found a QMetaObject without a className, generating a random name";
+    className = QByteArray("error-unknown-name-");
+    className.append(QByteArray::number(generatedNames.size()));
+    generatedNames.insert(mo, className);
+    return className;
+}
+
+QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, const QList<QQmlType *> &skip = QList<QQmlType *>())
 {
     QSet<const QMetaObject *> metas;
     metas.insert(FriendlyQObject::qtMeta());
 
     QHash<QByteArray, QSet<QByteArray> > extensions;
-    foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
+    foreach (const QQmlType *ty, QQmlMetaType::qmlTypes()) {
         qmlTypesByCppName[ty->metaObject()->className()].insert(ty);
         if (ty->isExtendedType()) {
             extensions[ty->typeName()].insert(ty->metaObject()->className());
@@ -157,52 +191,78 @@ QSet<const QMetaObject *> collectReachableMetaObjects(const QString &importCode,
         collectReachableMetaObjects(ty, &metas);
     }
 
-    // Adjust ids of extended objects.
-    // The chain ends up being:
-    // __extended__.originalname - the base object
-    // __extension_0_.originalname - first extension
-    // ..
-    // __extension_n-2_.originalname - second to last extension
-    // originalname - last extension
-    // ### does this actually work for multiple extensions? it seems like the prototypes might be wrong
-    foreach (const QByteArray &extendedCpp, extensions.keys()) {
-        cppToId.remove(extendedCpp);
-        const QByteArray extendedId = convertToId(extendedCpp);
-        cppToId.insert(extendedCpp, "__extended__." + extendedId);
-        QSet<QByteArray> extensionCppNames = extensions.value(extendedCpp);
-        int c = 0;
+    // Adjust exports of the base object if there are extensions.
+    // For each export of a base object there can be a single extension object overriding it.
+    // Example: QDeclarativeGraphicsWidget overrides the QtQuick/QGraphicsWidget export
+    //          of QGraphicsWidget.
+    foreach (const QByteArray &baseCpp, extensions.keys()) {
+        QSet<const QQmlType *> baseExports = qmlTypesByCppName.value(baseCpp);
+
+        const QSet<QByteArray> extensionCppNames = extensions.value(baseCpp);
         foreach (const QByteArray &extensionCppName, extensionCppNames) {
-            if (c != extensionCppNames.size() - 1) {
-                QByteArray adjustedName = QString("__extension__%1.%2").arg(QString::number(c), QString(extendedId)).toAscii();
-                cppToId.insert(extensionCppName, adjustedName);
-            } else {
-                cppToId.insert(extensionCppName, extendedId);
+            const QSet<const QQmlType *> extensionExports = qmlTypesByCppName.value(extensionCppName);
+
+            // remove extension exports from base imports
+            // unfortunately the QQmlType pointers don't match, so can't use QSet::substract
+            QSet<const QQmlType *> newBaseExports;
+            foreach (const QQmlType *baseExport, baseExports) {
+                bool match = false;
+                foreach (const QQmlType *extensionExport, extensionExports) {
+                    if (baseExport->qmlTypeName() == extensionExport->qmlTypeName()
+                            && baseExport->majorVersion() == extensionExport->majorVersion()
+                            && baseExport->minorVersion() == extensionExport->minorVersion()) {
+                        match = true;
+                        break;
+                    }
+                }
+                if (!match)
+                    newBaseExports.insert(baseExport);
             }
-            ++c;
+            baseExports = newBaseExports;
         }
+        qmlTypesByCppName[baseCpp] = baseExports;
     }
 
     // find even more QMetaObjects by instantiating QML types and running
     // over the instances
-    foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) {
+    foreach (QQmlType *ty, QQmlMetaType::qmlTypes()) {
+        if (skip.contains(ty))
+            continue;
         if (ty->isExtendedType())
             continue;
+        if (!ty->isCreatable())
+            continue;
+        if (ty->typeName() == "QQmlComponent")
+            continue;
 
-        QByteArray tyName = ty->qmlTypeName();
-        tyName = tyName.mid(tyName.lastIndexOf('/') + 1);
+        QString tyName = ty->qmlTypeName();
+        tyName = tyName.mid(tyName.lastIndexOf(QLatin1Char('/')) + 1);
+        if (tyName.isEmpty())
+            continue;
 
-        QByteArray code = importCode.toUtf8();
-        code += tyName;
-        code += " {}\n";
+        inObjectInstantiation = tyName;
+        QObject *object = 0;
 
-        QDeclarativeComponent c(engine);
-        c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typeinstance.qml"));
+        if (ty->isSingleton()) {
+            QQmlType::SingletonInstanceInfo *siinfo = ty->singletonInstanceInfo();
+            if (siinfo->qobjectCallback) {
+                siinfo->init(engine);
+                collectReachableMetaObjects(object, &metas);
+                object = siinfo->qobjectApi(engine);
+            } else {
+                inObjectInstantiation.clear();
+                continue; // we don't handle QJSValue singleton types.
+            }
+        } else {
+            ty->create();
+        }
+
+        inObjectInstantiation.clear();
 
-        QObject *object = c.create();
         if (object)
             collectReachableMetaObjects(object, &metas);
         else
-            qDebug() << "Could not create" << tyName << ":" << c.errorString();
+            qWarning() << "Could not create" << tyName;
     }
 
     return metas;
@@ -226,7 +286,7 @@ public:
     {
         qml->writeStartObject("Component");
 
-        QByteArray id = convertToId(meta->className());
+        QByteArray id = convertToId(meta);
         qml->writeScriptBinding(QLatin1String("name"), enquote(id));
 
         for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) {
@@ -238,55 +298,107 @@ public:
         }
 
         if (meta->superClass())
-            qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()->className())));
+            qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
 
-        QSet<const QDeclarativeType *> qmlTypes = qmlTypesByCppName.value(meta->className());
+        QSet<const QQmlType *> qmlTypes = qmlTypesByCppName.value(meta->className());
         if (!qmlTypes.isEmpty()) {
-            QStringList exports;
+            QHash<QString, const QQmlType *> exports;
 
-            foreach (const QDeclarativeType *qmlTy, qmlTypes) {
+            foreach (const QQmlType *qmlTy, qmlTypes) {
                 QString qmlTyName = qmlTy->qmlTypeName();
-                // some qmltype names are missing the actual names, ignore that import
-                if (qmlTyName.endsWith('/'))
-                    continue;
                 if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
                     qmlTyName.remove(0, relocatableModuleUri.size() + 1);
                 }
-                exports += enquote(QString("%1 %2.%3").arg(
-                                       qmlTyName,
-                                       QString::number(qmlTy->majorVersion()),
-                                       QString::number(qmlTy->minorVersion())));
+                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
-            exports.removeDuplicates();
-            qSort(exports);
-
-            qml->writeArrayBinding(QLatin1String("exports"), exports);
+            QStringList exportStrings = exports.keys();
+            qSort(exportStrings);
+            qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
+
+            // write meta object revisions unless they're all zero
+            QStringList metaObjectRevisions;
+            bool shouldWriteMetaObjectRevisions = false;
+            foreach (const QString &exportString, exportStrings) {
+                int metaObjectRevision = exports[exportString]->metaObjectRevision();
+                if (metaObjectRevision != 0)
+                    shouldWriteMetaObjectRevisions = true;
+                metaObjectRevisions += QString::number(metaObjectRevision);
+            }
+            if (shouldWriteMetaObjectRevisions)
+                qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
 
             if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) {
-                qml->writeScriptBinding(QLatin1String("attachedType"), enquote(
-                                            convertToId(attachedType->className())));
+                // 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));
 
-        for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index)
-            dump(meta->property(index));
+        QSet<QString> implicitSignals;
+        for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
+            const QMetaProperty &property = meta->property(index);
+            dump(property);
+            implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name())));
+        }
 
-        for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
-            dump(meta->method(index));
+        if (meta == &QObject::staticMetaObject) {
+            // for QObject, hide deleteLater() and onDestroyed
+            for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) {
+                QMetaMethod method = meta->method(index);
+                QByteArray signature = method.methodSignature();
+                if (signature == QByteArrayLiteral("destroyed(QObject*)")
+                        || signature == QByteArrayLiteral("destroyed()")
+                        || signature == QByteArrayLiteral("deleteLater()"))
+                    continue;
+                dump(method, implicitSignals);
+            }
+
+            // and add toString(), destroy() and destroy(int)
+            qml->writeStartObject(QLatin1String("Method"));
+            qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("toString")));
+            qml->writeEndObject();
+            qml->writeStartObject(QLatin1String("Method"));
+            qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
+            qml->writeEndObject();
+            qml->writeStartObject(QLatin1String("Method"));
+            qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
+            qml->writeStartObject(QLatin1String("Parameter"));
+            qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("delay")));
+            qml->writeScriptBinding(QLatin1String("type"), enquote(QLatin1String("int")));
+            qml->writeEndObject();
+            qml->writeEndObject();
+        } else {
+            for (int index = meta->methodOffset(); index < meta->methodCount(); ++index)
+                dump(meta->method(index), implicitSignals);
+        }
 
         qml->writeEndObject();
     }
 
     void writeEasingCurve()
     {
-        qml->writeStartObject("Component");
+        qml->writeStartObject(QLatin1String("Component"));
         qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve")));
-        qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QDeclarativeEasingValueType")));
+        qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QQmlEasingValueType")));
         qml->writeEndObject();
     }
 
@@ -301,7 +413,7 @@ private:
     */
     static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer)
     {
-        static QByteArray declListPrefix = "QDeclarativeListProperty<";
+        static QByteArray declListPrefix = "QQmlListProperty<";
 
         if (typeName->endsWith('*')) {
             *isPointer = true;
@@ -336,12 +448,16 @@ private:
         qml->writeStartObject("Property");
 
         qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
+#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
+        if (int revision = prop.revision())
+            qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
+#endif
         writeTypeProperties(prop.typeName(), prop.isWritable());
 
         qml->writeEndObject();
     }
 
-    void dump(const QMetaMethod &meth)
+    void dump(const QMetaMethod &meth, const QSet<QString> &implicitSignals)
     {
         if (meth.methodType() == QMetaMethod::Signal) {
             if (meth.access() != QMetaMethod::Protected)
@@ -350,12 +466,17 @@ private:
             return; // nothing to do.
         }
 
-        QByteArray name = meth.signature();
-        int lparenIndex = name.indexOf('(');
-        if (lparenIndex == -1) {
-            return; // invalid signature
+        QByteArray name = meth.name();
+        const QString typeName = convertToId(meth.typeName());
+
+        if (implicitSignals.contains(name)
+                && !meth.revision()
+                && meth.methodType() == QMetaMethod::Signal
+                && meth.parameterNames().isEmpty()
+                && typeName != QLatin1String("void")) {
+            // don't mention implicit signals
+            return;
         }
-        name = name.left(lparenIndex);
 
         if (meth.methodType() == QMetaMethod::Signal)
             qml->writeStartObject(QLatin1String("Signal"));
@@ -364,8 +485,12 @@ private:
 
         qml->writeScriptBinding(QLatin1String("name"), enquote(name));
 
-        const QString typeName = convertToId(meth.typeName());
-        if (! typeName.isEmpty())
+#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 4))
+        if (int revision = meth.revision())
+            qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision));
+#endif
+
+        if (typeName != QLatin1String("void"))
             qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
 
         for (int i = 0; i < meth.parameterTypes().size(); ++i) {
@@ -408,6 +533,8 @@ void sigSegvHandler(int) {
     fprintf(stderr, "Error: SEGV\n");
     if (!currentProperty.isEmpty())
         fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData());
+    if (!inObjectInstantiation.isEmpty())
+        fprintf(stderr, "While instantiating the object '%s'.\n", inObjectInstantiation.toLatin1().constData());
     exit(EXIT_SEGV);
 }
 #endif
@@ -415,9 +542,9 @@ void sigSegvHandler(int) {
 void printUsage(const QString &appName)
 {
     qWarning() << qPrintable(QString(
-                                 "Usage: %1 [-notrelocatable] module.uri version [module/import/path]\n"
-                                 "       %1 -path path/to/qmldir/directory [version]\n"
-                                 "       %1 -builtins\n"
+                                 "Usage: %1 [-v] [-notrelocatable] module.uri version [module/import/path]\n"
+                                 "       %1 [-v] -path path/to/qmldir/directory [version]\n"
+                                 "       %1 [-v] -builtins\n"
                                  "Example: %1 Qt.labs.particles 4.7 /home/user/dev/qt-install/imports").arg(
                                  appName));
 }
@@ -427,26 +554,24 @@ int main(int argc, char *argv[])
 #ifdef Q_OS_UNIX
     // qmldump may crash, but we don't want any crash handlers to pop up
     // therefore we intercept the segfault and just exit() ourselves
-    struct sigaction action;
+    struct sigaction sigAction;
 
-    sigemptyset(&action.sa_mask);
-    action.sa_handler = &sigSegvHandler;
-    action.sa_flags   = 0;
+    sigemptyset(&sigAction.sa_mask);
+    sigAction.sa_handler = &sigSegvHandler;
+    sigAction.sa_flags   = 0;
 
-    sigaction(SIGSEGV, &action, 0);
+    sigaction(SIGSEGV, &sigAction, 0);
 #endif
 
 #ifdef QT_SIMULATOR
     // Running this application would bring up the Qt Simulator (since it links QtGui), avoid that!
     QtSimulatorPrivate::SimulatorConnection::createStubInstance();
 #endif
-    QApplication app(argc, argv);
+
+    QGuiApplication app(argc, argv);
     const QStringList args = app.arguments();
     const QString appName = QFileInfo(app.applicationFilePath()).baseName();
-    if (!(args.size() >= 3
-          || (args.size() == 2
-              && (args.at(1) == QLatin1String("--builtins")
-                  || args.at(1) == QLatin1String("-builtins"))))) {
+    if (args.size() < 2) {
         printUsage(appName);
         return EXIT_INVALIDARGUMENTS;
     }
@@ -454,8 +579,9 @@ int main(int argc, char *argv[])
     QString pluginImportUri;
     QString pluginImportVersion;
     bool relocatable = true;
-    bool pathImport = false;
-    if (args.size() >= 3) {
+    enum Action { Uri, Path, Builtins };
+    Action action = Uri;
+    {
         QStringList positionalArgs;
         foreach (const QString &arg, args) {
             if (!arg.startsWith(QLatin1Char('-'))) {
@@ -468,14 +594,19 @@ int main(int argc, char *argv[])
                 relocatable = false;
             } else if (arg == QLatin1String("--path")
                        || arg == QLatin1String("-path")) {
-                pathImport = true;
+                action = Path;
+            } else if (arg == QLatin1String("--builtins")
+                       || arg == QLatin1String("-builtins")) {
+                action = Builtins;
+            } else if (arg == QLatin1String("-v")) {
+                verbose = true;
             } else {
                 qWarning() << "Invalid argument: " << arg;
                 return EXIT_INVALIDARGUMENTS;
             }
         }
 
-        if (!pathImport) {
+        if (action == Uri) {
             if (positionalArgs.size() != 3 && positionalArgs.size() != 4) {
                 qWarning() << "Incorrect number of positional arguments";
                 return EXIT_INVALIDARGUMENTS;
@@ -484,7 +615,7 @@ int main(int argc, char *argv[])
             pluginImportVersion = positionalArgs[2];
             if (positionalArgs.size() >= 4)
                 pluginImportPath = positionalArgs[3];
-        } else {
+        } else if (action == Path) {
             if (positionalArgs.size() != 2 && positionalArgs.size() != 3) {
                 qWarning() << "Incorrect number of positional arguments";
                 return EXIT_INVALIDARGUMENTS;
@@ -492,30 +623,70 @@ int main(int argc, char *argv[])
             pluginImportPath = QDir::fromNativeSeparators(positionalArgs[1]);
             if (positionalArgs.size() == 3)
                 pluginImportVersion = positionalArgs[2];
+        } else if (action == Builtins) {
+            if (positionalArgs.size() != 1) {
+                qWarning() << "Incorrect number of positional arguments";
+                return EXIT_INVALIDARGUMENTS;
+            }
         }
     }
 
-    QDeclarativeView view;
-    QDeclarativeEngine *engine = view.engine();
-    if (!pluginImportPath.isEmpty())
-        engine->addImportPath(pluginImportPath);
+    QQmlEngine engine;
+    if (!pluginImportPath.isEmpty()) {
+        QDir cur = QDir::current();
+        cur.cd(pluginImportPath);
+        pluginImportPath = cur.absolutePath();
+        QDir::setCurrent(pluginImportPath);
+        engine.addImportPath(pluginImportPath);
+    }
+
+    // load the QtQuick 2 plugin
+    {
+        QByteArray code("import QtQuick 2.0\nQtObject {}");
+        QQmlComponent c(&engine);
+        c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/loadqtquick2.qml"));
+        c.create();
+        if (!c.errors().isEmpty()) {
+            foreach (const QQmlError &error, c.errors())
+                qWarning() << error.toString();
+            return EXIT_IMPORTERROR;
+        }
+    }
 
     // find all QMetaObjects reachable from the builtin module
-    QByteArray importCode("import QtQuick 1.0\n");
-    QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(importCode, engine);
+    QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(&engine);
+    QList<QQmlType *> defaultTypes = QQmlMetaType::qmlTypes();
+
+    // add some otherwise unreachable QMetaObjects
+    defaultReachable.insert(&QQuickMouseEvent::staticMetaObject);
+    // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported
 
     // this will hold the meta objects we want to dump information of
     QSet<const QMetaObject *> metas;
 
-    if (pluginImportUri.isEmpty() && !pathImport) {
+    if (action == Builtins) {
         metas = defaultReachable;
     } else {
+        // find a valid QtQuick import
+        QByteArray importCode;
+        QQmlType *qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject);
+        if (!qtObjectType) {
+            qWarning() << "Could not find QtObject type";
+            importCode = QByteArray("import QtQuick 2.0\n");
+        } else {
+            QString module = qtObjectType->qmlTypeName();
+            module = module.mid(0, module.lastIndexOf(QLatin1Char('/')));
+            importCode = QString("import %1 %2.%3\n").arg(module,
+                                                          QString::number(qtObjectType->majorVersion()),
+                                                          QString::number(qtObjectType->minorVersion())).toUtf8();
+        }
+
         // find all QMetaObjects reachable when the specified module is imported
-        if (!pathImport) {
-            importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toAscii();
+        if (action != Path) {
+            importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toLatin1();
         } else {
             // pluginImportVersion can be empty
-            importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toAscii();
+            importCode += QString("import \".\" %2\n").arg(pluginImportVersion).toLatin1();
         }
 
         // create a component with these imports to make sure the imports are valid
@@ -523,18 +694,18 @@ int main(int argc, char *argv[])
         {
             QByteArray code = importCode;
             code += "QtObject {}";
-            QDeclarativeComponent c(engine);
+            QQmlComponent c(&engine);
 
             c.setData(code, QUrl::fromLocalFile(pluginImportPath + "/typelist.qml"));
             c.create();
             if (!c.errors().isEmpty()) {
-                foreach (const QDeclarativeError &error, c.errors())
+                foreach (const QQmlError &error, c.errors())
                     qWarning() << error.toString();
                 return EXIT_IMPORTERROR;
             }
         }
 
-        QSet<const QMetaObject *> candidates = collectReachableMetaObjects(importCode, engine);
+        QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, defaultTypes);
         candidates.subtract(defaultReachable);
 
         // Also eliminate meta objects with the same classname.
@@ -551,24 +722,26 @@ int main(int argc, char *argv[])
 
     // setup static rewrites of type names
     cppToId.insert("QString", "string");
-    cppToId.insert("QDeclarativeEasingValueType::Type", "Type");
+    cppToId.insert("QQmlEasingValueType::Type", "Type");
 
     // start dumping data
     QByteArray bytes;
     QmlStreamWriter qml(&bytes);
 
     qml.writeStartDocument();
-    qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 0);
-    qml.write("\n"
+    qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 1);
+    qml.write(QString("\n"
               "// This file describes the plugin-supplied types contained in the library.\n"
               "// It is used for QML tooling purposes only.\n"
-              "\n");
+              "//\n"
+              "// This file was auto-generated with the command '%1'.\n"
+              "\n").arg(args.join(QLatin1String(" "))));
     qml.writeStartObject("Module");
 
     // 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->className()), meta);
+        nameToMeta.insert(convertToId(meta), meta);
 
     Dumper dumper(&qml);
     if (relocatable)
@@ -577,7 +750,7 @@ int main(int argc, char *argv[])
         dumper.dump(meta);
     }
 
-    // define QEasingCurve as an extension of QDeclarativeEasingValueType, this way
+    // define QEasingCurve as an extension of QQmlEasingValueType, this way
     // properties using the QEasingCurve type get useful type information.
     if (pluginImportUri.isEmpty())
         dumper.writeEasingCurve();
@@ -585,7 +758,7 @@ int main(int argc, char *argv[])
     qml.writeEndObject();
     qml.writeEndDocument();
 
-    std::cout << bytes.constData();
+    std::cout << bytes.constData() << std::flush;
 
     // workaround to avoid crashes on exit
     QTimer timer;