Commit 27d08306 authored by Christian Kamm's avatar Christian Kamm

QmlJS: Move the exported-C++-type detection out of C++ code.

It now lives in qmljstools/qmljsfindexportedcpptypes, all in one place.

Also ensures that the source code is available when a file is being
scanned for QML exports. This will enable checking comments for
annotations about the URI a plugin is usually imported as.

Change-Id: I1da36d0678e0a8d34b171dbe0f6b5690d89eb18b
Reviewed-on: http://codereview.qt.nokia.com/3392Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@nokia.com>
parent c105ae47
This diff is collapsed.
......@@ -40,6 +40,7 @@
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtCore/QFileInfo>
#include <QtCore/QAtomicInt>
namespace CPlusPlus {
......@@ -125,11 +126,6 @@ public:
void check(CheckMode mode = FullCheck);
void findExposedQmlTypes();
void releaseSource();
void releaseTranslationUnit();
static Ptr create(const QString &fileName);
class DiagnosticMessage
......@@ -320,18 +316,8 @@ public:
const MacroUse *findMacroUseAt(unsigned offset) const;
const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
class ExportedQmlType {
public:
QString packageName;
QString typeName;
int majorVersion;
int minorVersion;
Scope *scope;
QString typeExpression;
};
QList<ExportedQmlType> exportedQmlTypes() const
{ return _exportedQmlTypes; }
void keepSourceAndAST();
void releaseSourceAndAST();
private:
QString _fileName;
......@@ -344,11 +330,12 @@ private:
QList<Block> _skippedBlocks;
QList<MacroUse> _macroUses;
QList<UndefinedMacroUse> _undefinedMacroUses;
QList<ExportedQmlType> _exportedQmlTypes;
QByteArray _source;
QDateTime _lastModified;
QAtomicInt _keepSourceAndASTCount;
unsigned _revision;
unsigned _editorRevision;
bool _fastCheck;
friend class Snapshot;
};
......
......@@ -140,8 +140,6 @@ public:
virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const = 0;
Q_SIGNALS:
void documentUpdated(CPlusPlus::Document::Ptr doc);
void sourceFilesRefreshed(const QStringList &files);
......
......@@ -502,7 +502,9 @@ void Link::loadImplicitDefaultImports(Imports *imports)
import.info = info;
import.object = new ObjectValue(d->valueOwner);
foreach (QmlObjectValue *object,
d->valueOwner->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
d->valueOwner->cppQmlTypes().typesForImport(
defaultPackage,
ComponentVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion))) {
import.object->setMember(object->className(), object);
}
d->importCache.insert(ImportCacheKey(info), import);
......
......@@ -290,15 +290,11 @@ public:
void operator()()
{
_doc->check(_mode);
_doc->findExposedQmlTypes();
_doc->releaseSource();
_doc->releaseTranslationUnit();
if (_mode == Document::FastCheck)
_doc->control()->squeeze();
if (_modelManager)
_modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
_doc->releaseSourceAndAST();
}
};
} // end of anonymous namespace
......@@ -590,6 +586,7 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
const QByteArray preprocessedCode = preprocess(fileName, contents);
doc->setSource(preprocessedCode);
doc->keepSourceAndAST();
doc->tokenize();
snapshot.insert(doc);
......@@ -1284,166 +1281,6 @@ void CppModelManager::GC()
protectSnapshot.unlock();
}
static FullySpecifiedType stripPointerAndReference(const FullySpecifiedType &type)
{
Type *t = type.type();
while (t) {
if (PointerType *ptr = t->asPointerType())
t = ptr->elementType().type();
else if (ReferenceType *ref = t->asReferenceType())
t = ref->elementType().type();
else
break;
}
return FullySpecifiedType(t);
}
static QString toQmlType(const FullySpecifiedType &type)
{
Overview overview;
QString result = overview(stripPointerAndReference(type));
if (result == QLatin1String("QString"))
result = QLatin1String("string");
return result;
}
static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpression &typeOf)
{
QList<LookupItem> results = typeOf(expression, scope);
Class *klass = 0;
foreach (const LookupItem &item, results) {
if (item.declaration()) {
klass = item.declaration()->asClass();
if (klass)
return klass;
}
}
return 0;
}
static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
TypeOfExpression &typeOf)
{
using namespace LanguageUtils;
Overview namePrinter;
classes->insert(klass, fmo);
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
if (!member->name())
continue;
if (Function *func = member->type()->asFunctionType()) {
if (!func->isSlot() && !func->isInvokable() && !func->isSignal())
continue;
FakeMetaMethod method(namePrinter(func->name()), toQmlType(func->returnType()));
if (func->isSignal())
method.setMethodType(FakeMetaMethod::Signal);
else
method.setMethodType(FakeMetaMethod::Slot);
for (unsigned a = 0; a < func->argumentCount(); ++a) {
Symbol *arg = func->argumentAt(a);
QString name(CppModelManager::tr("unnamed"));
if (arg->name())
name = namePrinter(arg->name());
method.addParameter(name, toQmlType(arg->type()));
}
fmo->addMethod(method);
}
if (QtPropertyDeclaration *propDecl = member->asQtPropertyDeclaration()) {
const FullySpecifiedType &type = propDecl->type();
const bool isList = false; // ### fixme
const bool isWritable = propDecl->flags() & QtPropertyDeclaration::WriteFunction;
const bool isPointer = type.type() && type.type()->isPointerType();
const int revision = 0; // ### fixme
FakeMetaProperty property(
namePrinter(propDecl->name()),
toQmlType(type),
isList, isWritable, isPointer,
revision);
fmo->addProperty(property);
}
if (QtEnum *qtEnum = member->asQtEnum()) {
// find the matching enum
Enum *e = 0;
QList<LookupItem> result = typeOf(namePrinter(qtEnum->name()), klass);
foreach (const LookupItem &item, result) {
if (item.declaration()) {
e = item.declaration()->asEnum();
if (e)
break;
}
}
if (!e)
continue;
FakeMetaEnum metaEnum(namePrinter(e->name()));
for (unsigned j = 0; j < e->memberCount(); ++j) {
Symbol *enumMember = e->memberAt(j);
if (!enumMember->name())
continue;
metaEnum.addKey(namePrinter(enumMember->name()), 0);
}
fmo->addEnum(metaEnum);
}
}
// only single inheritance is supported
if (klass->baseClassCount() > 0) {
BaseClass *base = klass->baseClassAt(0);
if (!base->name())
return;
const QString baseClassName = namePrinter(base->name());
fmo->setSuperclassName(baseClassName);
Class *baseClass = lookupClass(baseClassName, klass, typeOf);
if (!baseClass)
return;
FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
if (!baseFmo) {
baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
populate(baseFmo, baseClass, classes, typeOf);
}
}
}
QList<LanguageUtils::FakeMetaObject::ConstPtr> CppModelManager::exportedQmlObjects(const Document::Ptr &doc) const
{
using namespace LanguageUtils;
QList<FakeMetaObject::ConstPtr> exportedObjects;
QHash<Class *, FakeMetaObject::Ptr> classes;
const QList<CPlusPlus::Document::ExportedQmlType> exported = doc->exportedQmlTypes();
if (exported.isEmpty())
return exportedObjects;
TypeOfExpression typeOf;
const Snapshot currentSnapshot = snapshot();
typeOf.init(doc, currentSnapshot);
foreach (const Document::ExportedQmlType &exportedType, exported) {
FakeMetaObject::Ptr fmo(new FakeMetaObject);
fmo->addExport(exportedType.typeName, exportedType.packageName,
ComponentVersion(exportedType.majorVersion, exportedType.minorVersion));
exportedObjects += fmo;
Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
if (!klass)
continue;
// add the no-package export, so the cpp name can be used in properties
Overview overview;
fmo->addExport(overview(klass->name()), QString(), ComponentVersion());
populate(fmo, klass, &classes, typeOf);
}
return exportedObjects;
}
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
emit sourceFilesRefreshed(files);
......
......@@ -128,8 +128,6 @@ public:
virtual void findMacroUsages(const CPlusPlus::Macro &macro);
virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const;
void finishedRefreshingSourceFiles(const QStringList &files);
Q_SIGNALS:
......
This diff is collapsed.
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
#define QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
#include <cplusplus/CppDocument.h>
#include <languageutils/fakemetaobject.h>
namespace QmlJSTools {
class FindExportedCppTypes
{
public:
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
QList<LanguageUtils::FakeMetaObject::ConstPtr> operator()(const CPlusPlus::Document::Ptr &document);
static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
private:
CPlusPlus::Snapshot m_snapshot;
};
} // namespace QmlJSTools
#endif // QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
......@@ -33,6 +33,7 @@
#include "qmljsmodelmanager.h"
#include "qmljstoolsconstants.h"
#include "qmljsplugindumper.h"
#include "qmljsfindexportedcpptypes.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
......@@ -41,8 +42,6 @@
#include <coreplugin/messagemanager.h>
#include <cplusplus/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/TypeOfExpression.h>
#include <cplusplus/Overview.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/parser/qmldirparser_p.h>
......@@ -98,8 +97,10 @@ void ModelManager::delayedInitialization()
CPlusPlus::CppModelManagerInterface *cppModelManager =
CPlusPlus::CppModelManagerInterface::instance();
if (cppModelManager) {
// It's important to have a direct connection here so we can prevent
// the source and AST of the cpp document being cleaned away.
connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
this, SLOT(queueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)));
this, SLOT(maybeQueueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)), Qt::DirectConnection);
}
}
......@@ -673,9 +674,25 @@ void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &im
m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri, importVersion);
}
void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
// is called *inside a c++ parsing thread*, to allow hanging on to source and ast
void ModelManager::maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
{
m_queuedCppDocuments.insert(doc->fileName());
// keep source and AST alive if we want to scan for register calls
const bool scan = FindExportedCppTypes::maybeExportsTypes(doc);
if (scan)
doc->keepSourceAndAST();
// delegate actual queuing to the gui thread
QMetaObject::invokeMethod(this, "queueCppQmlTypeUpdate",
Q_ARG(CPlusPlus::Document::Ptr, doc), Q_ARG(bool, scan));
}
void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan)
{
QPair<CPlusPlus::Document::Ptr, bool> prev = m_queuedCppDocuments.value(doc->fileName());
if (prev.first && prev.second)
prev.first->releaseSourceAndAST();
m_queuedCppDocuments.insert(doc->fileName(), qMakePair(doc, scan));
m_updateCppQmlTypesTimer->start();
}
......@@ -691,20 +708,32 @@ void ModelManager::startCppQmlTypeUpdate()
m_queuedCppDocuments.clear();
}
void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files)
void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager,
CPlusPlus::CppModelManagerInterface *cppModelManager,
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents)
{
CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
FindExportedCppTypes finder(snapshot);
typedef QPair<CPlusPlus::Document::Ptr, bool> DocScanPair;
foreach (const DocScanPair &pair, documents) {
CPlusPlus::Document::Ptr doc = pair.first;
const bool scan = pair.second;
const QString fileName = doc->fileName();
if (!scan) {
newCppTypes.remove(fileName);
continue;
}
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder(doc);
foreach (const QString &fileName, files) {
CPlusPlus::Document::Ptr doc = snapshot.document(fileName);
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported;
if (doc)
exported = cppModelManager->exportedQmlObjects(doc);
if (!exported.isEmpty())
newCppTypes[fileName] = exported;
else
newCppTypes.remove(fileName);
doc->releaseSourceAndAST();
}
QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
......
......@@ -117,12 +117,15 @@ protected:
void updateImportPaths();
private slots:
void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan);
void startCppQmlTypeUpdate();
private:
static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
static void updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files);
static void updateCppQmlTypes(ModelManager *qmlModelManager,
CPlusPlus::CppModelManagerInterface *cppModelManager,
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents);
mutable QMutex m_mutex;
Core::ICore *m_core;
......@@ -134,7 +137,7 @@ private:
QFutureSynchronizer<void> m_synchronizer;
QTimer *m_updateCppQmlTypesTimer;
QSet<QString> m_queuedCppDocuments;
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
CppQmlTypeHash m_cppTypes;
mutable QMutex m_cppTypesMutex;
......
......@@ -17,7 +17,8 @@ HEADERS += \
$$PWD/qmljsfunctionfilter.h \
$$PWD/qmljslocatordata.h \
$$PWD/qmljsindenter.h \
$$PWD/qmljscodestylesettingspage.h
$$PWD/qmljscodestylesettingspage.h \
$$PWD/qmljsfindexportedcpptypes.h
SOURCES += \
$$PWD/qmljstoolsplugin.cpp \
......@@ -30,7 +31,8 @@ SOURCES += \
$$PWD/qmljsfunctionfilter.cpp \
$$PWD/qmljslocatordata.cpp \
$$PWD/qmljsindenter.cpp \
$$PWD/qmljscodestylesettingspage.cpp
$$PWD/qmljscodestylesettingspage.cpp \
$$PWD/qmljsfindexportedcpptypes.cpp
FORMS += \
$$PWD/qmljscodestylesettingspage.ui
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment