forked from qt-creator/qt-creator
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/3392 Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
@@ -256,7 +256,8 @@ Document::Document(const QString &fileName)
|
|||||||
: _fileName(QDir::cleanPath(fileName)),
|
: _fileName(QDir::cleanPath(fileName)),
|
||||||
_globalNamespace(0),
|
_globalNamespace(0),
|
||||||
_revision(0),
|
_revision(0),
|
||||||
_editorRevision(0)
|
_editorRevision(0),
|
||||||
|
_fastCheck(false)
|
||||||
{
|
{
|
||||||
_control = new Control();
|
_control = new Control();
|
||||||
|
|
||||||
@@ -574,8 +575,10 @@ void Document::check(CheckMode mode)
|
|||||||
|
|
||||||
_globalNamespace = _control->newNamespace(0);
|
_globalNamespace = _control->newNamespace(0);
|
||||||
Bind semantic(_translationUnit);
|
Bind semantic(_translationUnit);
|
||||||
if (mode == FastCheck)
|
if (mode == FastCheck) {
|
||||||
|
_fastCheck = true;
|
||||||
semantic.setSkipFunctionBodies(true);
|
semantic.setSkipFunctionBodies(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (! _translationUnit->ast())
|
if (! _translationUnit->ast())
|
||||||
return; // nothing to do.
|
return; // nothing to do.
|
||||||
@@ -589,253 +592,19 @@ void Document::check(CheckMode mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FindExposedQmlTypes : protected ASTVisitor
|
void Document::keepSourceAndAST()
|
||||||
{
|
{
|
||||||
Document *_doc;
|
_keepSourceAndASTCount.ref();
|
||||||
QList<Document::ExportedQmlType> _exportedTypes;
|
}
|
||||||
CompoundStatementAST *_compound;
|
|
||||||
ASTMatcher _matcher;
|
|
||||||
ASTPatternBuilder _builder;
|
|
||||||
Overview _overview;
|
|
||||||
|
|
||||||
public:
|
void Document::releaseSourceAndAST()
|
||||||
FindExposedQmlTypes(Document *doc)
|
|
||||||
: ASTVisitor(doc->translationUnit())
|
|
||||||
, _doc(doc)
|
|
||||||
, _compound(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QList<Document::ExportedQmlType> operator()()
|
|
||||||
{
|
|
||||||
_exportedTypes.clear();
|
|
||||||
accept(translationUnit()->ast());
|
|
||||||
return _exportedTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool visit(CompoundStatementAST *ast)
|
|
||||||
{
|
|
||||||
CompoundStatementAST *old = _compound;
|
|
||||||
_compound = ast;
|
|
||||||
accept(ast->statement_list);
|
|
||||||
_compound = old;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool visit(CallAST *ast)
|
|
||||||
{
|
|
||||||
IdExpressionAST *idExp = ast->base_expression->asIdExpression();
|
|
||||||
if (!idExp || !idExp->name)
|
|
||||||
return false;
|
|
||||||
TemplateIdAST *templateId = idExp->name->asTemplateId();
|
|
||||||
if (!templateId || !templateId->identifier_token)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// check the name
|
|
||||||
const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
|
|
||||||
if (!templateIdentifier)
|
|
||||||
return false;
|
|
||||||
const QString callName = QString::fromUtf8(templateIdentifier->chars());
|
|
||||||
if (callName != QLatin1String("qmlRegisterType"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// must have a single typeid template argument
|
|
||||||
if (!templateId->template_argument_list || !templateId->template_argument_list->value
|
|
||||||
|| templateId->template_argument_list->next)
|
|
||||||
return false;
|
|
||||||
TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
|
|
||||||
if (!typeId)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// must have four arguments
|
|
||||||
if (!ast->expression_list
|
|
||||||
|| !ast->expression_list->value || !ast->expression_list->next
|
|
||||||
|| !ast->expression_list->next->value || !ast->expression_list->next->next
|
|
||||||
|| !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
|
|
||||||
|| !ast->expression_list->next->next->next->value
|
|
||||||
|| ast->expression_list->next->next->next->next)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// last argument must be a string literal
|
|
||||||
const StringLiteral *nameLit = 0;
|
|
||||||
if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
|
|
||||||
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
|
||||||
if (!nameLit) {
|
|
||||||
// disable this warning for now, we don't want to encourage using string literals if they don't mean to
|
|
||||||
// in the future, we will also accept annotations for the qmlRegisterType arguments in comments
|
|
||||||
// translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
|
|
||||||
// "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the first argument is a string literal, things are easy
|
|
||||||
QString packageName;
|
|
||||||
if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
|
|
||||||
const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
|
|
||||||
packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
|
|
||||||
}
|
|
||||||
// as a special case, allow an identifier package argument if there's a
|
|
||||||
// Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
|
|
||||||
// in the enclosing compound statement
|
|
||||||
IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
|
|
||||||
if (packageName.isEmpty() && uriName && _compound) {
|
|
||||||
for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
|
|
||||||
StatementAST *stmt = it->value;
|
|
||||||
|
|
||||||
packageName = nameOfUriAssert(stmt, uriName);
|
|
||||||
if (!packageName.isEmpty())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// second and third argument must be integer literals
|
|
||||||
const NumericLiteral *majorLit = 0;
|
|
||||||
const NumericLiteral *minorLit = 0;
|
|
||||||
if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
|
|
||||||
majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
|
|
||||||
if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
|
|
||||||
minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
|
|
||||||
|
|
||||||
// build the descriptor
|
|
||||||
Document::ExportedQmlType exportedType;
|
|
||||||
exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
|
|
||||||
if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
|
|
||||||
exportedType.packageName = packageName;
|
|
||||||
exportedType.majorVersion = QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt();
|
|
||||||
exportedType.minorVersion = QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt();
|
|
||||||
} else {
|
|
||||||
// disable this warning, see above for details
|
|
||||||
// translationUnit()->warning(ast->base_expression->firstToken(),
|
|
||||||
// "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
|
|
||||||
// "cannot be determined by static analysis. The type will still be available globally.");
|
|
||||||
exportedType.packageName = QLatin1String("<default>");
|
|
||||||
}
|
|
||||||
|
|
||||||
// we want to do lookup later, so also store the surrounding scope
|
|
||||||
unsigned line, column;
|
|
||||||
translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
|
|
||||||
exportedType.scope = _doc->scopeAt(line, column);
|
|
||||||
|
|
||||||
// and the expression
|
|
||||||
const Token begin = translationUnit()->tokenAt(typeId->firstToken());
|
|
||||||
const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
|
|
||||||
exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
|
|
||||||
|
|
||||||
_exportedTypes += exportedType;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString stringOf(AST *ast)
|
|
||||||
{
|
|
||||||
const Token begin = translationUnit()->tokenAt(ast->firstToken());
|
|
||||||
const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
|
|
||||||
return _doc->source().mid(begin.begin(), last.end() - begin.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionAST *skipStringCall(ExpressionAST *exp)
|
|
||||||
{
|
|
||||||
if (!exp)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
IdExpressionAST *callName = _builder.IdExpression();
|
|
||||||
CallAST *call = _builder.Call(callName);
|
|
||||||
if (!exp->match(call, &_matcher))
|
|
||||||
return exp;
|
|
||||||
|
|
||||||
const QString name = stringOf(callName);
|
|
||||||
if (name != QLatin1String("QLatin1String")
|
|
||||||
&& name != QLatin1String("QString"))
|
|
||||||
return exp;
|
|
||||||
|
|
||||||
if (!call->expression_list || call->expression_list->next)
|
|
||||||
return exp;
|
|
||||||
|
|
||||||
return call->expression_list->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
|
|
||||||
{
|
|
||||||
QString null;
|
|
||||||
|
|
||||||
IdExpressionAST *outerCallName = _builder.IdExpression();
|
|
||||||
BinaryExpressionAST *binary = _builder.BinaryExpression();
|
|
||||||
// assert(... == ...);
|
|
||||||
ExpressionStatementAST *pattern = _builder.ExpressionStatement(
|
|
||||||
_builder.Call(outerCallName, _builder.ExpressionList(
|
|
||||||
binary)));
|
|
||||||
|
|
||||||
if (!stmt->match(pattern, &_matcher)) {
|
|
||||||
outerCallName = _builder.IdExpression();
|
|
||||||
binary = _builder.BinaryExpression();
|
|
||||||
// the expansion of Q_ASSERT(...),
|
|
||||||
// ((!(... == ...)) ? qt_assert(...) : ...);
|
|
||||||
pattern = _builder.ExpressionStatement(
|
|
||||||
_builder.NestedExpression(
|
|
||||||
_builder.ConditionalExpression(
|
|
||||||
_builder.NestedExpression(
|
|
||||||
_builder.UnaryExpression(
|
|
||||||
_builder.NestedExpression(
|
|
||||||
binary))),
|
|
||||||
_builder.Call(outerCallName))));
|
|
||||||
|
|
||||||
if (!stmt->match(pattern, &_matcher))
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString outerCall = stringOf(outerCallName);
|
|
||||||
if (outerCall != QLatin1String("qt_assert")
|
|
||||||
&& outerCall != QLatin1String("assert")
|
|
||||||
&& outerCall != QLatin1String("Q_ASSERT"))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
|
|
||||||
ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
|
|
||||||
if (!lhsExp || !rhsExp)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
StringLiteralAST *uriString = lhsExp->asStringLiteral();
|
|
||||||
IdExpressionAST *uriArgName = lhsExp->asIdExpression();
|
|
||||||
if (!uriString)
|
|
||||||
uriString = rhsExp->asStringLiteral();
|
|
||||||
if (!uriArgName)
|
|
||||||
uriArgName = rhsExp->asIdExpression();
|
|
||||||
if (!uriString || !uriArgName)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (stringOf(uriArgName) != stringOf(uriName))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
|
|
||||||
return QString::fromUtf8(packageLit->chars(), packageLit->size());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Document::findExposedQmlTypes()
|
|
||||||
{
|
|
||||||
if (! _translationUnit->ast())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
|
||||||
if (_translationUnit->control()->findIdentifier(
|
|
||||||
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
|
||||||
FindExposedQmlTypes finder(this);
|
|
||||||
_exportedQmlTypes = finder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::releaseSource()
|
|
||||||
{
|
{
|
||||||
|
if (!_keepSourceAndASTCount.deref()) {
|
||||||
_source.clear();
|
_source.clear();
|
||||||
}
|
|
||||||
|
|
||||||
void Document::releaseTranslationUnit()
|
|
||||||
{
|
|
||||||
_translationUnit->release();
|
_translationUnit->release();
|
||||||
|
if (_fastCheck)
|
||||||
|
_control->squeeze();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Snapshot::Snapshot()
|
Snapshot::Snapshot()
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QHash>
|
#include <QtCore/QHash>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QAtomicInt>
|
||||||
|
|
||||||
namespace CPlusPlus {
|
namespace CPlusPlus {
|
||||||
|
|
||||||
@@ -125,11 +126,6 @@ public:
|
|||||||
|
|
||||||
void check(CheckMode mode = FullCheck);
|
void check(CheckMode mode = FullCheck);
|
||||||
|
|
||||||
void findExposedQmlTypes();
|
|
||||||
|
|
||||||
void releaseSource();
|
|
||||||
void releaseTranslationUnit();
|
|
||||||
|
|
||||||
static Ptr create(const QString &fileName);
|
static Ptr create(const QString &fileName);
|
||||||
|
|
||||||
class DiagnosticMessage
|
class DiagnosticMessage
|
||||||
@@ -320,18 +316,8 @@ public:
|
|||||||
const MacroUse *findMacroUseAt(unsigned offset) const;
|
const MacroUse *findMacroUseAt(unsigned offset) const;
|
||||||
const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
|
const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
|
||||||
|
|
||||||
class ExportedQmlType {
|
void keepSourceAndAST();
|
||||||
public:
|
void releaseSourceAndAST();
|
||||||
QString packageName;
|
|
||||||
QString typeName;
|
|
||||||
int majorVersion;
|
|
||||||
int minorVersion;
|
|
||||||
Scope *scope;
|
|
||||||
QString typeExpression;
|
|
||||||
};
|
|
||||||
|
|
||||||
QList<ExportedQmlType> exportedQmlTypes() const
|
|
||||||
{ return _exportedQmlTypes; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _fileName;
|
QString _fileName;
|
||||||
@@ -344,11 +330,12 @@ private:
|
|||||||
QList<Block> _skippedBlocks;
|
QList<Block> _skippedBlocks;
|
||||||
QList<MacroUse> _macroUses;
|
QList<MacroUse> _macroUses;
|
||||||
QList<UndefinedMacroUse> _undefinedMacroUses;
|
QList<UndefinedMacroUse> _undefinedMacroUses;
|
||||||
QList<ExportedQmlType> _exportedQmlTypes;
|
|
||||||
QByteArray _source;
|
QByteArray _source;
|
||||||
QDateTime _lastModified;
|
QDateTime _lastModified;
|
||||||
|
QAtomicInt _keepSourceAndASTCount;
|
||||||
unsigned _revision;
|
unsigned _revision;
|
||||||
unsigned _editorRevision;
|
unsigned _editorRevision;
|
||||||
|
bool _fastCheck;
|
||||||
|
|
||||||
friend class Snapshot;
|
friend class Snapshot;
|
||||||
};
|
};
|
||||||
|
@@ -140,8 +140,6 @@ public:
|
|||||||
|
|
||||||
virtual void findMacroUsages(const CPlusPlus::Macro ¯o) = 0;
|
virtual void findMacroUsages(const CPlusPlus::Macro ¯o) = 0;
|
||||||
|
|
||||||
virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const = 0;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void documentUpdated(CPlusPlus::Document::Ptr doc);
|
void documentUpdated(CPlusPlus::Document::Ptr doc);
|
||||||
void sourceFilesRefreshed(const QStringList &files);
|
void sourceFilesRefreshed(const QStringList &files);
|
||||||
|
@@ -502,7 +502,9 @@ void Link::loadImplicitDefaultImports(Imports *imports)
|
|||||||
import.info = info;
|
import.info = info;
|
||||||
import.object = new ObjectValue(d->valueOwner);
|
import.object = new ObjectValue(d->valueOwner);
|
||||||
foreach (QmlObjectValue *object,
|
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);
|
import.object->setMember(object->className(), object);
|
||||||
}
|
}
|
||||||
d->importCache.insert(ImportCacheKey(info), import);
|
d->importCache.insert(ImportCacheKey(info), import);
|
||||||
|
@@ -290,15 +290,11 @@ public:
|
|||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
_doc->check(_mode);
|
_doc->check(_mode);
|
||||||
_doc->findExposedQmlTypes();
|
|
||||||
_doc->releaseSource();
|
|
||||||
_doc->releaseTranslationUnit();
|
|
||||||
|
|
||||||
if (_mode == Document::FastCheck)
|
|
||||||
_doc->control()->squeeze();
|
|
||||||
|
|
||||||
if (_modelManager)
|
if (_modelManager)
|
||||||
_modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
|
_modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
|
||||||
|
|
||||||
|
_doc->releaseSourceAndAST();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
@@ -590,6 +586,7 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
|
|||||||
const QByteArray preprocessedCode = preprocess(fileName, contents);
|
const QByteArray preprocessedCode = preprocess(fileName, contents);
|
||||||
|
|
||||||
doc->setSource(preprocessedCode);
|
doc->setSource(preprocessedCode);
|
||||||
|
doc->keepSourceAndAST();
|
||||||
doc->tokenize();
|
doc->tokenize();
|
||||||
|
|
||||||
snapshot.insert(doc);
|
snapshot.insert(doc);
|
||||||
@@ -1284,166 +1281,6 @@ void CppModelManager::GC()
|
|||||||
protectSnapshot.unlock();
|
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)
|
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
|
||||||
{
|
{
|
||||||
emit sourceFilesRefreshed(files);
|
emit sourceFilesRefreshed(files);
|
||||||
|
@@ -128,8 +128,6 @@ public:
|
|||||||
|
|
||||||
virtual void findMacroUsages(const CPlusPlus::Macro ¯o);
|
virtual void findMacroUsages(const CPlusPlus::Macro ¯o);
|
||||||
|
|
||||||
virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const;
|
|
||||||
|
|
||||||
void finishedRefreshingSourceFiles(const QStringList &files);
|
void finishedRefreshingSourceFiles(const QStringList &files);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
486
src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
Normal file
486
src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "qmljsfindexportedcpptypes.h"
|
||||||
|
|
||||||
|
#include <qmljs/qmljsinterpreter.h>
|
||||||
|
#include <cplusplus/AST.h>
|
||||||
|
#include <cplusplus/TranslationUnit.h>
|
||||||
|
#include <cplusplus/ASTVisitor.h>
|
||||||
|
#include <cplusplus/ASTMatcher.h>
|
||||||
|
#include <cplusplus/ASTPatternBuilder.h>
|
||||||
|
#include <cplusplus/Overview.h>
|
||||||
|
#include <cplusplus/TypeOfExpression.h>
|
||||||
|
#include <cplusplus/Names.h>
|
||||||
|
#include <cplusplus/Literals.h>
|
||||||
|
#include <cplusplus/CoreTypes.h>
|
||||||
|
#include <cplusplus/Symbols.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
using namespace QmlJSTools;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using namespace CPlusPlus;
|
||||||
|
|
||||||
|
class ExportedQmlType {
|
||||||
|
public:
|
||||||
|
QString packageName;
|
||||||
|
QString typeName;
|
||||||
|
LanguageUtils::ComponentVersion version;
|
||||||
|
Scope *scope;
|
||||||
|
QString typeExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FindExportsVisitor : protected ASTVisitor
|
||||||
|
{
|
||||||
|
CPlusPlus::Document::Ptr _doc;
|
||||||
|
QList<ExportedQmlType> _exportedTypes;
|
||||||
|
CompoundStatementAST *_compound;
|
||||||
|
ASTMatcher _matcher;
|
||||||
|
ASTPatternBuilder _builder;
|
||||||
|
Overview _overview;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FindExportsVisitor(CPlusPlus::Document::Ptr doc)
|
||||||
|
: ASTVisitor(doc->translationUnit())
|
||||||
|
, _doc(doc)
|
||||||
|
, _compound(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QList<ExportedQmlType> operator()()
|
||||||
|
{
|
||||||
|
_exportedTypes.clear();
|
||||||
|
accept(translationUnit()->ast());
|
||||||
|
return _exportedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool visit(CompoundStatementAST *ast)
|
||||||
|
{
|
||||||
|
CompoundStatementAST *old = _compound;
|
||||||
|
_compound = ast;
|
||||||
|
accept(ast->statement_list);
|
||||||
|
_compound = old;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool visit(CallAST *ast)
|
||||||
|
{
|
||||||
|
IdExpressionAST *idExp = ast->base_expression->asIdExpression();
|
||||||
|
if (!idExp || !idExp->name)
|
||||||
|
return false;
|
||||||
|
TemplateIdAST *templateId = idExp->name->asTemplateId();
|
||||||
|
if (!templateId || !templateId->identifier_token)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check the name
|
||||||
|
const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
|
||||||
|
if (!templateIdentifier)
|
||||||
|
return false;
|
||||||
|
const QString callName = QString::fromUtf8(templateIdentifier->chars());
|
||||||
|
if (callName != QLatin1String("qmlRegisterType"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// must have a single typeid template argument
|
||||||
|
if (!templateId->template_argument_list || !templateId->template_argument_list->value
|
||||||
|
|| templateId->template_argument_list->next)
|
||||||
|
return false;
|
||||||
|
TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
|
||||||
|
if (!typeId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// must have four arguments
|
||||||
|
if (!ast->expression_list
|
||||||
|
|| !ast->expression_list->value || !ast->expression_list->next
|
||||||
|
|| !ast->expression_list->next->value || !ast->expression_list->next->next
|
||||||
|
|| !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
|
||||||
|
|| !ast->expression_list->next->next->next->value
|
||||||
|
|| ast->expression_list->next->next->next->next)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// last argument must be a string literal
|
||||||
|
const StringLiteral *nameLit = 0;
|
||||||
|
if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
|
||||||
|
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
||||||
|
if (!nameLit) {
|
||||||
|
// disable this warning for now, we don't want to encourage using string literals if they don't mean to
|
||||||
|
// in the future, we will also accept annotations for the qmlRegisterType arguments in comments
|
||||||
|
// translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
|
||||||
|
// "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the first argument is a string literal, things are easy
|
||||||
|
QString packageName;
|
||||||
|
if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
|
||||||
|
const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
|
||||||
|
packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
|
||||||
|
}
|
||||||
|
// as a special case, allow an identifier package argument if there's a
|
||||||
|
// Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
|
||||||
|
// in the enclosing compound statement
|
||||||
|
IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
|
||||||
|
if (packageName.isEmpty() && uriName && _compound) {
|
||||||
|
for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
|
||||||
|
StatementAST *stmt = it->value;
|
||||||
|
|
||||||
|
packageName = nameOfUriAssert(stmt, uriName);
|
||||||
|
if (!packageName.isEmpty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// second and third argument must be integer literals
|
||||||
|
const NumericLiteral *majorLit = 0;
|
||||||
|
const NumericLiteral *minorLit = 0;
|
||||||
|
if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
|
||||||
|
majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
|
||||||
|
if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
|
||||||
|
minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
|
||||||
|
|
||||||
|
// build the descriptor
|
||||||
|
ExportedQmlType exportedType;
|
||||||
|
exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
|
||||||
|
if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
|
||||||
|
exportedType.packageName = packageName;
|
||||||
|
exportedType.version = LanguageUtils::ComponentVersion(
|
||||||
|
QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt(),
|
||||||
|
QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt());
|
||||||
|
} else {
|
||||||
|
// disable this warning, see above for details
|
||||||
|
// translationUnit()->warning(ast->base_expression->firstToken(),
|
||||||
|
// "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
|
||||||
|
// "cannot be determined by static analysis. The type will still be available globally.");
|
||||||
|
exportedType.packageName = QmlJS::CppQmlTypes::defaultPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to do lookup later, so also store the surrounding scope
|
||||||
|
unsigned line, column;
|
||||||
|
translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
|
||||||
|
exportedType.scope = _doc->scopeAt(line, column);
|
||||||
|
|
||||||
|
// and the expression
|
||||||
|
const Token begin = translationUnit()->tokenAt(typeId->firstToken());
|
||||||
|
const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
|
||||||
|
exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
|
||||||
|
|
||||||
|
_exportedTypes += exportedType;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString stringOf(AST *ast)
|
||||||
|
{
|
||||||
|
const Token begin = translationUnit()->tokenAt(ast->firstToken());
|
||||||
|
const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
|
||||||
|
return _doc->source().mid(begin.begin(), last.end() - begin.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionAST *skipStringCall(ExpressionAST *exp)
|
||||||
|
{
|
||||||
|
if (!exp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
IdExpressionAST *callName = _builder.IdExpression();
|
||||||
|
CallAST *call = _builder.Call(callName);
|
||||||
|
if (!exp->match(call, &_matcher))
|
||||||
|
return exp;
|
||||||
|
|
||||||
|
const QString name = stringOf(callName);
|
||||||
|
if (name != QLatin1String("QLatin1String")
|
||||||
|
&& name != QLatin1String("QString"))
|
||||||
|
return exp;
|
||||||
|
|
||||||
|
if (!call->expression_list || call->expression_list->next)
|
||||||
|
return exp;
|
||||||
|
|
||||||
|
return call->expression_list->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
|
||||||
|
{
|
||||||
|
QString null;
|
||||||
|
|
||||||
|
IdExpressionAST *outerCallName = _builder.IdExpression();
|
||||||
|
BinaryExpressionAST *binary = _builder.BinaryExpression();
|
||||||
|
// assert(... == ...);
|
||||||
|
ExpressionStatementAST *pattern = _builder.ExpressionStatement(
|
||||||
|
_builder.Call(outerCallName, _builder.ExpressionList(
|
||||||
|
binary)));
|
||||||
|
|
||||||
|
if (!stmt->match(pattern, &_matcher)) {
|
||||||
|
outerCallName = _builder.IdExpression();
|
||||||
|
binary = _builder.BinaryExpression();
|
||||||
|
// the expansion of Q_ASSERT(...),
|
||||||
|
// ((!(... == ...)) ? qt_assert(...) : ...);
|
||||||
|
pattern = _builder.ExpressionStatement(
|
||||||
|
_builder.NestedExpression(
|
||||||
|
_builder.ConditionalExpression(
|
||||||
|
_builder.NestedExpression(
|
||||||
|
_builder.UnaryExpression(
|
||||||
|
_builder.NestedExpression(
|
||||||
|
binary))),
|
||||||
|
_builder.Call(outerCallName))));
|
||||||
|
|
||||||
|
if (!stmt->match(pattern, &_matcher))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString outerCall = stringOf(outerCallName);
|
||||||
|
if (outerCall != QLatin1String("qt_assert")
|
||||||
|
&& outerCall != QLatin1String("assert")
|
||||||
|
&& outerCall != QLatin1String("Q_ASSERT"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
|
||||||
|
ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
|
||||||
|
if (!lhsExp || !rhsExp)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
StringLiteralAST *uriString = lhsExp->asStringLiteral();
|
||||||
|
IdExpressionAST *uriArgName = lhsExp->asIdExpression();
|
||||||
|
if (!uriString)
|
||||||
|
uriString = rhsExp->asStringLiteral();
|
||||||
|
if (!uriArgName)
|
||||||
|
uriArgName = rhsExp->asIdExpression();
|
||||||
|
if (!uriString || !uriArgName)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (stringOf(uriArgName) != stringOf(uriName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
|
||||||
|
return QString::fromUtf8(packageLit->chars(), packageLit->size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
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> exportedQmlObjects(
|
||||||
|
const Document::Ptr &doc,
|
||||||
|
const Snapshot &snapshot,
|
||||||
|
const QList<ExportedQmlType> &exportedTypes)
|
||||||
|
{
|
||||||
|
using namespace LanguageUtils;
|
||||||
|
QList<FakeMetaObject::ConstPtr> exportedObjects;
|
||||||
|
QHash<Class *, FakeMetaObject::Ptr> classes;
|
||||||
|
|
||||||
|
if (exportedTypes.isEmpty())
|
||||||
|
return exportedObjects;
|
||||||
|
|
||||||
|
TypeOfExpression typeOf;
|
||||||
|
typeOf.init(doc, snapshot);
|
||||||
|
foreach (const ExportedQmlType &exportedType, exportedTypes) {
|
||||||
|
FakeMetaObject::Ptr fmo(new FakeMetaObject);
|
||||||
|
fmo->addExport(exportedType.typeName,
|
||||||
|
exportedType.packageName,
|
||||||
|
exportedType.version);
|
||||||
|
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()), QmlJS::CppQmlTypes::cppPackage, ComponentVersion());
|
||||||
|
|
||||||
|
populate(fmo, klass, &classes, typeOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportedObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
|
||||||
|
: m_snapshot(snapshot)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||||
|
{
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> noResults;
|
||||||
|
if (document->source().isEmpty()
|
||||||
|
|| !document->translationUnit()->ast())
|
||||||
|
return noResults;
|
||||||
|
|
||||||
|
FindExportsVisitor finder(document);
|
||||||
|
QList<ExportedQmlType> exports = finder();
|
||||||
|
if (exports.isEmpty())
|
||||||
|
return noResults;
|
||||||
|
|
||||||
|
return exportedQmlObjects(document, m_snapshot, exports);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
|
||||||
|
{
|
||||||
|
if (!document->control())
|
||||||
|
return false;
|
||||||
|
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
||||||
|
if (document->control()->findIdentifier(
|
||||||
|
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
55
src/plugins/qmljstools/qmljsfindexportedcpptypes.h
Normal file
55
src/plugins/qmljstools/qmljsfindexportedcpptypes.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "qmljsmodelmanager.h"
|
||||||
#include "qmljstoolsconstants.h"
|
#include "qmljstoolsconstants.h"
|
||||||
#include "qmljsplugindumper.h"
|
#include "qmljsplugindumper.h"
|
||||||
|
#include "qmljsfindexportedcpptypes.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
@@ -41,8 +42,6 @@
|
|||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
#include <cplusplus/ModelManagerInterface.h>
|
#include <cplusplus/ModelManagerInterface.h>
|
||||||
#include <cplusplus/CppDocument.h>
|
#include <cplusplus/CppDocument.h>
|
||||||
#include <cplusplus/TypeOfExpression.h>
|
|
||||||
#include <cplusplus/Overview.h>
|
|
||||||
#include <qmljs/qmljscontext.h>
|
#include <qmljs/qmljscontext.h>
|
||||||
#include <qmljs/qmljsbind.h>
|
#include <qmljs/qmljsbind.h>
|
||||||
#include <qmljs/parser/qmldirparser_p.h>
|
#include <qmljs/parser/qmldirparser_p.h>
|
||||||
@@ -98,8 +97,10 @@ void ModelManager::delayedInitialization()
|
|||||||
CPlusPlus::CppModelManagerInterface *cppModelManager =
|
CPlusPlus::CppModelManagerInterface *cppModelManager =
|
||||||
CPlusPlus::CppModelManagerInterface::instance();
|
CPlusPlus::CppModelManagerInterface::instance();
|
||||||
if (cppModelManager) {
|
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)),
|
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);
|
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();
|
m_updateCppQmlTypesTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,20 +708,32 @@ void ModelManager::startCppQmlTypeUpdate()
|
|||||||
m_queuedCppDocuments.clear();
|
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();
|
CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
|
||||||
CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
|
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())
|
if (!exported.isEmpty())
|
||||||
newCppTypes[fileName] = exported;
|
newCppTypes[fileName] = exported;
|
||||||
else
|
else
|
||||||
newCppTypes.remove(fileName);
|
newCppTypes.remove(fileName);
|
||||||
|
|
||||||
|
doc->releaseSourceAndAST();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
|
QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
|
||||||
|
@@ -117,12 +117,15 @@ protected:
|
|||||||
void updateImportPaths();
|
void updateImportPaths();
|
||||||
|
|
||||||
private slots:
|
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();
|
void startCppQmlTypeUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
|
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;
|
mutable QMutex m_mutex;
|
||||||
Core::ICore *m_core;
|
Core::ICore *m_core;
|
||||||
@@ -134,7 +137,7 @@ private:
|
|||||||
QFutureSynchronizer<void> m_synchronizer;
|
QFutureSynchronizer<void> m_synchronizer;
|
||||||
|
|
||||||
QTimer *m_updateCppQmlTypesTimer;
|
QTimer *m_updateCppQmlTypesTimer;
|
||||||
QSet<QString> m_queuedCppDocuments;
|
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
|
||||||
CppQmlTypeHash m_cppTypes;
|
CppQmlTypeHash m_cppTypes;
|
||||||
mutable QMutex m_cppTypesMutex;
|
mutable QMutex m_cppTypesMutex;
|
||||||
|
|
||||||
|
@@ -17,7 +17,8 @@ HEADERS += \
|
|||||||
$$PWD/qmljsfunctionfilter.h \
|
$$PWD/qmljsfunctionfilter.h \
|
||||||
$$PWD/qmljslocatordata.h \
|
$$PWD/qmljslocatordata.h \
|
||||||
$$PWD/qmljsindenter.h \
|
$$PWD/qmljsindenter.h \
|
||||||
$$PWD/qmljscodestylesettingspage.h
|
$$PWD/qmljscodestylesettingspage.h \
|
||||||
|
$$PWD/qmljsfindexportedcpptypes.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/qmljstoolsplugin.cpp \
|
$$PWD/qmljstoolsplugin.cpp \
|
||||||
@@ -30,7 +31,8 @@ SOURCES += \
|
|||||||
$$PWD/qmljsfunctionfilter.cpp \
|
$$PWD/qmljsfunctionfilter.cpp \
|
||||||
$$PWD/qmljslocatordata.cpp \
|
$$PWD/qmljslocatordata.cpp \
|
||||||
$$PWD/qmljsindenter.cpp \
|
$$PWD/qmljsindenter.cpp \
|
||||||
$$PWD/qmljscodestylesettingspage.cpp
|
$$PWD/qmljscodestylesettingspage.cpp \
|
||||||
|
$$PWD/qmljsfindexportedcpptypes.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
$$PWD/qmljscodestylesettingspage.ui
|
$$PWD/qmljscodestylesettingspage.ui
|
||||||
|
Reference in New Issue
Block a user