forked from qt-creator/qt-creator
qml: moving most of ModelManager logic to ModelManageInterface
Currently ModelManager contains lot logic, but as it sits in QmlJSTools it is not possible to use it in standalone tests. Moving most of the logic to ModelManagerInterface (and cleanup) to allow better testing, and refactoring. This introduces a dependency of the qmljs lib on the cplusplus lib Also a (small) part of the CppTool::ModelManagerInterface has been moved to CPlusPlus::CppModelManagerBase to remove the dependency on CppTools to gather the Qml types exposed from C++. Change-Id: Icad7fe96dfd0f1a2b1058d82bd98c77c40aa5e9d Reviewed-by: Fawzi Mohamed <fawzi.mohamed@digia.com>
This commit is contained in:
@@ -11,6 +11,7 @@ HEADERS += \
|
||||
$$PWD/qmljsbind.h \
|
||||
$$PWD/qmljsbundle.h \
|
||||
$$PWD/qmljsevaluate.h \
|
||||
$$PWD/qmljsfindexportedcpptypes.h \
|
||||
$$PWD/qmljsdocument.h \
|
||||
$$PWD/qmljsscanner.h \
|
||||
$$PWD/qmljsinterpreter.h \
|
||||
@@ -25,6 +26,7 @@ HEADERS += \
|
||||
$$PWD/qmljsrewriter.h \
|
||||
$$PWD/qmljsicons.h \
|
||||
$$PWD/qmljsdelta.h \
|
||||
$$PWD/qmljsplugindumper.h \
|
||||
$$PWD/qmljstypedescriptionreader.h \
|
||||
$$PWD/qmljsscopeastpath.h \
|
||||
$$PWD/qmljsvalueowner.h \
|
||||
@@ -47,6 +49,7 @@ SOURCES += \
|
||||
$$PWD/qmljsbind.cpp \
|
||||
$$PWD/qmljsbundle.cpp \
|
||||
$$PWD/qmljsevaluate.cpp \
|
||||
$$PWD/qmljsfindexportedcpptypes.cpp \
|
||||
$$PWD/qmljsdocument.cpp \
|
||||
$$PWD/qmljsscanner.cpp \
|
||||
$$PWD/qmljsinterpreter.cpp \
|
||||
@@ -60,6 +63,7 @@ SOURCES += \
|
||||
$$PWD/qmljsrewriter.cpp \
|
||||
$$PWD/qmljsicons.cpp \
|
||||
$$PWD/qmljsdelta.cpp \
|
||||
$$PWD/qmljsplugindumper.cpp \
|
||||
$$PWD/qmljstypedescriptionreader.cpp \
|
||||
$$PWD/qmljsscopeastpath.cpp \
|
||||
$$PWD/qmljsvalueowner.cpp \
|
||||
|
||||
@@ -11,6 +11,7 @@ QtcLibrary {
|
||||
|
||||
Depends { name: "Utils" }
|
||||
Depends { name: "LanguageUtils" }
|
||||
Depends { name: "CPlusPlus" }
|
||||
Depends { name: "Qt"; submodules: ["widgets", "script", "xml"] }
|
||||
|
||||
Group {
|
||||
@@ -33,6 +34,7 @@ QtcLibrary {
|
||||
"qmljsdelta.cpp", "qmljsdelta.h",
|
||||
"qmljsdocument.cpp", "qmljsdocument.h",
|
||||
"qmljsevaluate.cpp", "qmljsevaluate.h",
|
||||
"qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h",
|
||||
"qmljsicons.cpp", "qmljsicons.h",
|
||||
"qmljsicontextpane.h",
|
||||
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
|
||||
@@ -41,6 +43,7 @@ QtcLibrary {
|
||||
"qmljslineinfo.cpp", "qmljslineinfo.h",
|
||||
"qmljslink.cpp", "qmljslink.h",
|
||||
"qmljsmodelmanagerinterface.cpp", "qmljsmodelmanagerinterface.h",
|
||||
"qmljsplugindumper.cpp", "qmljsplugindumper.h",
|
||||
"qmljspropertyreader.cpp", "qmljspropertyreader.h",
|
||||
"qmljsqrcparser.cpp", "qmljsqrcparser.h",
|
||||
"qmljsreformatter.cpp", "qmljsreformatter.h",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
QTC_LIB_NAME = QmlJS
|
||||
QTC_LIB_DEPENDS += \
|
||||
utils \
|
||||
languageutils
|
||||
languageutils \
|
||||
cplusplus
|
||||
|
||||
@@ -80,5 +80,11 @@ enum Enum
|
||||
};
|
||||
}
|
||||
|
||||
namespace Constants {
|
||||
|
||||
const char TASK_INDEX[] = "QmlJSEditor.TaskIndex";
|
||||
const char TASK_IMPORT_SCAN[] = "QmlJSEditor.TaskImportScan";
|
||||
|
||||
} // namespace Constants
|
||||
} // namespace QmlJS
|
||||
#endif // QMLJSCONSTANTS_H
|
||||
|
||||
777
src/libs/qmljs/qmljsfindexportedcpptypes.cpp
Normal file
777
src/libs/qmljs/qmljsfindexportedcpptypes.cpp
Normal file
@@ -0,0 +1,777 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmljsfindexportedcpptypes.h"
|
||||
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <cplusplus/TypeOfExpression.h>
|
||||
#include <cplusplus/cppmodelmanagerbase.h>
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
|
||||
//using namespace QmlJS;
|
||||
|
||||
namespace {
|
||||
using namespace CPlusPlus;
|
||||
|
||||
class ExportedQmlType {
|
||||
public:
|
||||
QString packageName;
|
||||
QString typeName;
|
||||
LanguageUtils::ComponentVersion version;
|
||||
Scope *scope;
|
||||
QString typeExpression;
|
||||
};
|
||||
|
||||
class ContextProperty {
|
||||
public:
|
||||
QString name;
|
||||
QString expression;
|
||||
unsigned line, column;
|
||||
};
|
||||
|
||||
class FindExportsVisitor : protected ASTVisitor
|
||||
{
|
||||
CPlusPlus::Document::Ptr _doc;
|
||||
QList<ExportedQmlType> _exportedTypes;
|
||||
QList<ContextProperty> _contextProperties;
|
||||
CompoundStatementAST *_compound;
|
||||
ASTMatcher _matcher;
|
||||
ASTPatternBuilder _builder;
|
||||
Overview _overview;
|
||||
QList<CPlusPlus::Document::DiagnosticMessage> _messages;
|
||||
|
||||
public:
|
||||
FindExportsVisitor(CPlusPlus::Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit())
|
||||
, _doc(doc)
|
||||
, _compound(0)
|
||||
{}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
_exportedTypes.clear();
|
||||
_contextProperties.clear();
|
||||
accept(translationUnit()->ast());
|
||||
}
|
||||
|
||||
QList<CPlusPlus::Document::DiagnosticMessage> messages() const
|
||||
{
|
||||
return _messages;
|
||||
}
|
||||
|
||||
QList<ExportedQmlType> exportedTypes() const
|
||||
{
|
||||
return _exportedTypes;
|
||||
}
|
||||
|
||||
QList<ContextProperty> contextProperties() const
|
||||
{
|
||||
return _contextProperties;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool visit(CompoundStatementAST *ast)
|
||||
{
|
||||
CompoundStatementAST *old = _compound;
|
||||
_compound = ast;
|
||||
accept(ast->statement_list);
|
||||
_compound = old;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(CallAST *ast)
|
||||
{
|
||||
if (checkForQmlRegisterType(ast))
|
||||
return false;
|
||||
checkForSetContextProperty(ast);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkForQmlRegisterType(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());
|
||||
int argCount = 0;
|
||||
if (callName == QLatin1String("qmlRegisterType"))
|
||||
argCount = 4;
|
||||
else if (callName == QLatin1String("qmlRegisterUncreatableType"))
|
||||
argCount = 5;
|
||||
else
|
||||
return false;
|
||||
|
||||
// check that there is a typeId
|
||||
if (!templateId->template_argument_list || !templateId->template_argument_list->value)
|
||||
return false;
|
||||
// sometimes there can be a second argument, the metaRevisionNumber
|
||||
if (templateId->template_argument_list->next) {
|
||||
if (!templateId->template_argument_list->next->value ||
|
||||
templateId->template_argument_list->next->next)
|
||||
return false;
|
||||
// should just check for a generic ExpressionAST?
|
||||
NumericLiteralAST *metaRevision =
|
||||
templateId->template_argument_list->next->value->asNumericLiteral();
|
||||
if (!metaRevision)
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
|
||||
if (!typeId)
|
||||
return false;
|
||||
|
||||
// must have four arguments for qmlRegisterType and five for qmlRegisterUncreatableType
|
||||
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)
|
||||
return false;
|
||||
if (argCount == 4 && ast->expression_list->next->next->next->next)
|
||||
return false;
|
||||
if (argCount == 5 && (!ast->expression_list->next->next->next->next
|
||||
|| !ast->expression_list->next->next->next->next->value
|
||||
|| ast->expression_list->next->next->next->next->next))
|
||||
return false;
|
||||
|
||||
// 4th argument must be a string literal
|
||||
const StringLiteral *nameLit = 0;
|
||||
if (StringLiteralAST *nameAst = skipStringCall(ast->expression_list->next->next->next->value)->asStringLiteral())
|
||||
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
||||
if (!nameLit) {
|
||||
unsigned line, column;
|
||||
translationUnit()->getTokenStartPosition(ast->expression_list->next->next->next->value->firstToken(), &line, &column);
|
||||
_messages += Document::DiagnosticMessage(
|
||||
Document::DiagnosticMessage::Warning,
|
||||
_doc->fileName(),
|
||||
line, column,
|
||||
QmlJS::FindExportedCppTypes::tr(
|
||||
"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 = skipStringCall(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
|
||||
QString uriNameString;
|
||||
if (IdExpressionAST *uriName = ast->expression_list->value->asIdExpression())
|
||||
uriNameString = stringOf(uriName);
|
||||
if (packageName.isEmpty() && !uriNameString.isEmpty() && _compound) {
|
||||
for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
|
||||
StatementAST *stmt = it->value;
|
||||
|
||||
packageName = nameOfUriAssert(stmt, uriNameString);
|
||||
if (!packageName.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (packageName.isEmpty() && _compound) {
|
||||
// check the comments in _compound for annotations
|
||||
QRegExp uriAnnotation(QLatin1String("@uri\\s*([\\w\\.]*)"));
|
||||
|
||||
// scan every comment between the pipes in
|
||||
// {|
|
||||
// // comment
|
||||
// othercode;
|
||||
// |qmlRegisterType<Foo>(...);
|
||||
const Token begin = _doc->translationUnit()->tokenAt(_compound->firstToken());
|
||||
const Token end = _doc->translationUnit()->tokenAt(ast->firstToken());
|
||||
|
||||
// go through comments backwards to find the annotation closest to the call
|
||||
for (unsigned i = _doc->translationUnit()->commentCount(); i-- > 0; ) {
|
||||
const Token commentToken = _doc->translationUnit()->commentAt(i);
|
||||
if (commentToken.begin() >= end.begin() || commentToken.end() <= begin.begin())
|
||||
continue;
|
||||
const QString comment = stringOf(commentToken);
|
||||
if (uriAnnotation.indexIn(comment) != -1) {
|
||||
packageName = uriAnnotation.cap(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packageName.isEmpty()) {
|
||||
packageName = QmlJS::CppQmlTypes::defaultPackage;
|
||||
unsigned line, column;
|
||||
translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
|
||||
_messages += Document::DiagnosticMessage(
|
||||
Document::DiagnosticMessage::Warning,
|
||||
_doc->fileName(),
|
||||
line, column,
|
||||
QmlJS::FindExportedCppTypes::tr(
|
||||
"The module URI cannot be determined by static analysis. The type will be available\n"
|
||||
"globally in the QML editor. You can add a \"// @uri My.Module.Uri\" annotation to let\n"
|
||||
"Qt Creator know about a likely URI."));
|
||||
}
|
||||
|
||||
// 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());
|
||||
exportedType.packageName = packageName;
|
||||
if (majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
|
||||
exportedType.version = LanguageUtils::ComponentVersion(
|
||||
QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt(),
|
||||
QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt());
|
||||
}
|
||||
|
||||
// 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 = QString::fromUtf8(_doc->utf8Source().mid(begin.begin(), last.end() - begin.begin()));
|
||||
|
||||
_exportedTypes += exportedType;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static NameAST *callName(ExpressionAST *exp)
|
||||
{
|
||||
if (IdExpressionAST *idExp = exp->asIdExpression())
|
||||
return idExp->name;
|
||||
if (MemberAccessAST *memberExp = exp->asMemberAccess())
|
||||
return memberExp->member_name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ExpressionAST *skipQVariant(ExpressionAST *ast, TranslationUnit *translationUnit)
|
||||
{
|
||||
CallAST *call = ast->asCall();
|
||||
if (!call)
|
||||
return ast;
|
||||
if (!call->expression_list
|
||||
|| !call->expression_list->value
|
||||
|| call->expression_list->next)
|
||||
return ast;
|
||||
|
||||
IdExpressionAST *idExp = call->base_expression->asIdExpression();
|
||||
if (!idExp || !idExp->name)
|
||||
return ast;
|
||||
|
||||
// QVariant(foo) -> foo
|
||||
if (SimpleNameAST *simpleName = idExp->name->asSimpleName()) {
|
||||
const Identifier *id = translationUnit->identifier(simpleName->identifier_token);
|
||||
if (!id)
|
||||
return ast;
|
||||
if (QString::fromUtf8(id->chars(), id->size()) != QLatin1String("QVariant"))
|
||||
return ast;
|
||||
return call->expression_list->value;
|
||||
// QVariant::fromValue(foo) -> foo
|
||||
} else if (QualifiedNameAST *q = idExp->name->asQualifiedName()) {
|
||||
SimpleNameAST *simpleRhsName = q->unqualified_name->asSimpleName();
|
||||
if (!simpleRhsName
|
||||
|| !q->nested_name_specifier_list
|
||||
|| !q->nested_name_specifier_list->value
|
||||
|| q->nested_name_specifier_list->next)
|
||||
return ast;
|
||||
const Identifier *rhsId = translationUnit->identifier(simpleRhsName->identifier_token);
|
||||
if (!rhsId)
|
||||
return ast;
|
||||
if (QString::fromUtf8(rhsId->chars(), rhsId->size()) != QLatin1String("fromValue"))
|
||||
return ast;
|
||||
NestedNameSpecifierAST *nested = q->nested_name_specifier_list->value;
|
||||
SimpleNameAST *simpleLhsName = nested->class_or_namespace_name->asSimpleName();
|
||||
if (!simpleLhsName)
|
||||
return ast;
|
||||
const Identifier *lhsId = translationUnit->identifier(simpleLhsName->identifier_token);
|
||||
if (!lhsId)
|
||||
return ast;
|
||||
if (QString::fromUtf8(lhsId->chars(), lhsId->size()) != QLatin1String("QVariant"))
|
||||
return ast;
|
||||
return call->expression_list->value;
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
bool checkForSetContextProperty(CallAST *ast)
|
||||
{
|
||||
// check whether ast->base_expression has the 'setContextProperty' name
|
||||
NameAST *callNameAst = callName(ast->base_expression);
|
||||
if (!callNameAst)
|
||||
return false;
|
||||
SimpleNameAST *simpleNameAst = callNameAst->asSimpleName();
|
||||
if (!simpleNameAst || !simpleNameAst->identifier_token)
|
||||
return false;
|
||||
const Identifier *nameIdentifier = translationUnit()->identifier(simpleNameAst->identifier_token);
|
||||
if (!nameIdentifier)
|
||||
return false;
|
||||
const QString callName = QString::fromUtf8(nameIdentifier->chars(), nameIdentifier->size());
|
||||
if (callName != QLatin1String("setContextProperty"))
|
||||
return false;
|
||||
|
||||
// must have two arguments
|
||||
if (!ast->expression_list
|
||||
|| !ast->expression_list->value || !ast->expression_list->next
|
||||
|| !ast->expression_list->next->value || ast->expression_list->next->next)
|
||||
return false;
|
||||
|
||||
// first argument must be a string literal
|
||||
const StringLiteral *nameLit = 0;
|
||||
if (StringLiteralAST *nameAst = skipStringCall(ast->expression_list->value)->asStringLiteral())
|
||||
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
||||
if (!nameLit) {
|
||||
unsigned line, column;
|
||||
translationUnit()->getTokenStartPosition(ast->expression_list->value->firstToken(), &line, &column);
|
||||
_messages += Document::DiagnosticMessage(
|
||||
Document::DiagnosticMessage::Warning,
|
||||
_doc->fileName(),
|
||||
line, column,
|
||||
QmlJS::FindExportedCppTypes::tr(
|
||||
"must be a string literal to be available in the QML editor"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ContextProperty contextProperty;
|
||||
contextProperty.name = QString::fromUtf8(nameLit->chars(), nameLit->size());
|
||||
contextProperty.expression = stringOf(skipQVariant(ast->expression_list->next->value, translationUnit()));
|
||||
// we want to do lookup later, so also store the line and column of the target scope
|
||||
translationUnit()->getTokenStartPosition(ast->firstToken(),
|
||||
&contextProperty.line,
|
||||
&contextProperty.column);
|
||||
|
||||
_contextProperties += contextProperty;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QString stringOf(CPlusPlus::AST *ast)
|
||||
{
|
||||
return stringOf(ast->firstToken(), ast->lastToken() - 1);
|
||||
}
|
||||
|
||||
QString stringOf(int first, int last)
|
||||
{
|
||||
const Token firstToken = translationUnit()->tokenAt(first);
|
||||
const Token lastToken = translationUnit()->tokenAt(last);
|
||||
return QString::fromUtf8(_doc->utf8Source().mid(firstToken.begin(), lastToken.end() - firstToken.begin()));
|
||||
}
|
||||
|
||||
QString stringOf(const Token &token)
|
||||
{
|
||||
return QString::fromUtf8(_doc->utf8Source().mid(token.begin(), token.length()));
|
||||
}
|
||||
|
||||
ExpressionAST *skipStringCall(ExpressionAST *exp)
|
||||
{
|
||||
if (!exp || !exp->asCall())
|
||||
return exp;
|
||||
|
||||
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, const QString &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) != 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.prettyType(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.toUtf8(), scope);
|
||||
Class *klass = 0;
|
||||
foreach (const LookupItem &item, results) {
|
||||
if (item.declaration()) {
|
||||
klass = item.declaration()->asClass();
|
||||
if (klass)
|
||||
return klass;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LanguageUtils::FakeMetaObject::Ptr buildFakeMetaObject(
|
||||
Class *klass,
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects,
|
||||
TypeOfExpression &typeOf)
|
||||
{
|
||||
using namespace LanguageUtils;
|
||||
|
||||
if (FakeMetaObject::Ptr fmo = fakeMetaObjects->value(klass))
|
||||
return fmo;
|
||||
|
||||
FakeMetaObject::Ptr fmo(new FakeMetaObject);
|
||||
if (!klass)
|
||||
return fmo;
|
||||
fakeMetaObjects->insert(klass, fmo);
|
||||
|
||||
Overview namePrinter;
|
||||
|
||||
fmo->setClassName(namePrinter.prettyName(klass->name()));
|
||||
// add the no-package export, so the cpp name can be used in properties
|
||||
fmo->addExport(fmo->className(), QmlJS::CppQmlTypes::cppPackage, ComponentVersion());
|
||||
|
||||
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.prettyName(func->name()), toQmlType(func->returnType()));
|
||||
if (func->isSignal())
|
||||
method.setMethodType(FakeMetaMethod::Signal);
|
||||
else
|
||||
method.setMethodType(FakeMetaMethod::Slot);
|
||||
for (unsigned a = 0, argc = func->argumentCount(); a < argc; ++a) {
|
||||
Symbol *arg = func->argumentAt(a);
|
||||
QString name;
|
||||
if (arg->name())
|
||||
name = namePrinter.prettyName(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.prettyName(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.prettyName(qtEnum->name()).toUtf8(), klass);
|
||||
foreach (const LookupItem &item, result) {
|
||||
if (item.declaration()) {
|
||||
e = item.declaration()->asEnum();
|
||||
if (e)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
FakeMetaEnum metaEnum(namePrinter.prettyName(e->name()));
|
||||
for (unsigned j = 0; j < e->memberCount(); ++j) {
|
||||
Symbol *enumMember = e->memberAt(j);
|
||||
if (!enumMember->name())
|
||||
continue;
|
||||
metaEnum.addKey(namePrinter.prettyName(enumMember->name()), 0);
|
||||
}
|
||||
fmo->addEnum(metaEnum);
|
||||
}
|
||||
}
|
||||
|
||||
// only single inheritance is supported
|
||||
if (klass->baseClassCount() > 0) {
|
||||
BaseClass *base = klass->baseClassAt(0);
|
||||
if (!base->name())
|
||||
return fmo;
|
||||
const QString baseClassName = namePrinter.prettyName(base->name());
|
||||
fmo->setSuperclassName(baseClassName);
|
||||
|
||||
Class *baseClass = lookupClass(baseClassName, klass, typeOf);
|
||||
if (!baseClass)
|
||||
return fmo;
|
||||
buildFakeMetaObject(baseClass, fakeMetaObjects, typeOf);
|
||||
}
|
||||
|
||||
return fmo;
|
||||
}
|
||||
|
||||
static void buildExportedQmlObjects(
|
||||
TypeOfExpression &typeOf,
|
||||
const QList<ExportedQmlType> &cppExports,
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects)
|
||||
{
|
||||
using namespace LanguageUtils;
|
||||
|
||||
if (cppExports.isEmpty())
|
||||
return;
|
||||
|
||||
foreach (const ExportedQmlType &exportedType, cppExports) {
|
||||
Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
|
||||
// accepts a null klass
|
||||
FakeMetaObject::Ptr fmo = buildFakeMetaObject(klass, fakeMetaObjects, typeOf);
|
||||
fmo->addExport(exportedType.typeName,
|
||||
exportedType.packageName,
|
||||
exportedType.version);
|
||||
}
|
||||
}
|
||||
|
||||
static void buildContextProperties(
|
||||
const Document::Ptr &doc,
|
||||
TypeOfExpression &typeOf,
|
||||
const QList<ContextProperty> &contextPropertyDescriptions,
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects,
|
||||
QHash<QString, QString> *contextProperties)
|
||||
{
|
||||
using namespace LanguageUtils;
|
||||
|
||||
foreach (const ContextProperty &property, contextPropertyDescriptions) {
|
||||
Scope *scope = doc->scopeAt(property.line, property.column);
|
||||
QList<LookupItem> results = typeOf(property.expression.toUtf8(), scope);
|
||||
QString typeName;
|
||||
if (!results.isEmpty()) {
|
||||
LookupItem result = results.first();
|
||||
FullySpecifiedType simpleType = stripPointerAndReference(result.type());
|
||||
if (NamedType *namedType = simpleType.type()->asNamedType()) {
|
||||
Scope *typeScope = result.scope();
|
||||
if (!typeScope)
|
||||
typeScope = scope; // incorrect but may be an ok fallback
|
||||
ClassOrNamespace *binding = typeOf.context().lookupType(namedType->name(), typeScope);
|
||||
if (binding && !binding->symbols().isEmpty()) {
|
||||
// find the best 'Class' symbol
|
||||
for (int i = binding->symbols().size() - 1; i >= 0; --i) {
|
||||
if (Class *klass = binding->symbols().at(i)->asClass()) {
|
||||
FakeMetaObject::Ptr fmo = buildFakeMetaObject(klass, fakeMetaObjects, typeOf);
|
||||
typeName = fmo->className();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contextProperties->insert(property.name, typeName);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
|
||||
: m_snapshot(snapshot)
|
||||
{
|
||||
}
|
||||
|
||||
void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||
{
|
||||
m_contextProperties.clear();
|
||||
m_exportedTypes.clear();
|
||||
|
||||
// this check only guards against some input errors, if document's source and AST has not
|
||||
// been guarded properly the source and AST may still become empty/null while this function is running
|
||||
if (document->utf8Source().isEmpty()
|
||||
|| !document->translationUnit()->ast())
|
||||
return;
|
||||
|
||||
FindExportsVisitor finder(document);
|
||||
finder();
|
||||
static const QString kindKey = QLatin1String("QmlJSTools.ExportedQmlTypesDiagnostic");
|
||||
CppModelManagerBase::trySetExtraDiagnostics(document->fileName(), kindKey,
|
||||
finder.messages());
|
||||
|
||||
// if nothing was found, done
|
||||
const QList<ContextProperty> contextPropertyDescriptions = finder.contextProperties();
|
||||
const QList<ExportedQmlType> exports = finder.exportedTypes();
|
||||
if (exports.isEmpty() && contextPropertyDescriptions.isEmpty())
|
||||
return;
|
||||
|
||||
// context properties need lookup inside function scope, and thus require a full check
|
||||
CPlusPlus::Document::Ptr localDoc = document;
|
||||
if (document->checkMode() != CPlusPlus::Document::FullCheck && !contextPropertyDescriptions.isEmpty()) {
|
||||
localDoc = m_snapshot.documentFromSource(document->utf8Source(), document->fileName());
|
||||
localDoc->check();
|
||||
}
|
||||
|
||||
// create a single type of expression (and bindings) for the document
|
||||
TypeOfExpression typeOf;
|
||||
typeOf.init(localDoc, m_snapshot);
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> fakeMetaObjects;
|
||||
|
||||
// generate the exports from qmlRegisterType
|
||||
buildExportedQmlObjects(typeOf, exports, &fakeMetaObjects);
|
||||
|
||||
// add the types from the context properties and create a name->cppname map
|
||||
// also expose types where necessary
|
||||
buildContextProperties(localDoc, typeOf, contextPropertyDescriptions,
|
||||
&fakeMetaObjects, &m_contextProperties);
|
||||
|
||||
// convert to list of FakeMetaObject::ConstPtr
|
||||
m_exportedTypes.reserve(fakeMetaObjects.size());
|
||||
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) {
|
||||
fmo->updateFingerprint();
|
||||
m_exportedTypes += fmo;
|
||||
}
|
||||
}
|
||||
|
||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const
|
||||
{
|
||||
return m_exportedTypes;
|
||||
}
|
||||
|
||||
QHash<QString, QString> FindExportedCppTypes::contextProperties() const
|
||||
{
|
||||
return m_contextProperties;
|
||||
}
|
||||
|
||||
bool FindExportedCppTypes::maybeExportsTypes(const CPlusPlus::Document::Ptr &document)
|
||||
{
|
||||
if (!document->control())
|
||||
return false;
|
||||
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
||||
const QByteArray qmlRegisterUncreatableTypeToken("qmlRegisterUncreatableType");
|
||||
const QByteArray setContextPropertyToken("setContextProperty");
|
||||
if (document->control()->findIdentifier(
|
||||
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
||||
return true;
|
||||
}
|
||||
if (document->control()->findIdentifier(
|
||||
qmlRegisterUncreatableTypeToken.constData(), qmlRegisterUncreatableTypeToken.size())) {
|
||||
return true;
|
||||
}
|
||||
if (document->control()->findIdentifier(
|
||||
setContextPropertyToken.constData(), setContextPropertyToken.size())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace QmlJS
|
||||
64
src/libs/qmljs/qmljsfindexportedcpptypes.h
Normal file
64
src/libs/qmljs/qmljsfindexportedcpptypes.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLJS_QMLJSFINDEXPORTEDCPPTYPES_H
|
||||
#define QMLJS_QMLJSFINDEXPORTEDCPPTYPES_H
|
||||
|
||||
#include "qmljs_global.h"
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <languageutils/fakemetaobject.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHash>
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class QMLJS_EXPORT FindExportedCppTypes
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QmlJSTools::FindExportedCppTypes)
|
||||
public:
|
||||
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
|
||||
|
||||
// document must have a valid source and ast for the duration of the call
|
||||
void operator()(const CPlusPlus::Document::Ptr &document);
|
||||
|
||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedTypes() const;
|
||||
QHash<QString, QString> contextProperties() const;
|
||||
|
||||
static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
|
||||
|
||||
private:
|
||||
CPlusPlus::Snapshot m_snapshot;
|
||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> m_exportedTypes;
|
||||
QHash<QString, QString> m_contextProperties;
|
||||
};
|
||||
|
||||
} // namespace QmlJS
|
||||
|
||||
#endif // QMLJS_QMLJSFINDEXPORTEDCPPTYPES_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,21 +31,31 @@
|
||||
#define QMLJSMODELMANAGERINTERFACE_H
|
||||
|
||||
#include "qmljs_global.h"
|
||||
#include "qmljsdocument.h"
|
||||
#include "qmljsbundle.h"
|
||||
#include "qmljsconstants.h"
|
||||
#include "qmljsdocument.h"
|
||||
#include "qmljsqrcparser.h"
|
||||
#include "qmljsviewercontext.h"
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureSynchronizer>
|
||||
#include <QHash>
|
||||
#include <QMultiHash>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
|
||||
namespace ProjectExplorer { class Project; }
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class Snapshot;
|
||||
class PluginDumper;
|
||||
|
||||
class QMLJS_EXPORT ModelManagerInterface: public QObject
|
||||
{
|
||||
@@ -140,61 +150,130 @@ public:
|
||||
static Language::Enum guessLanguageOfFile(const QString &fileName);
|
||||
static QStringList globPatternsForLanguages(const QList<Language::Enum> languages);
|
||||
static ModelManagerInterface *instance();
|
||||
static void writeWarning(const QString &msg);
|
||||
static WorkingCopy workingCopy();
|
||||
|
||||
virtual WorkingCopy workingCopy() const = 0;
|
||||
QmlJS::Snapshot snapshot() const;
|
||||
QmlJS::Snapshot newestSnapshot() const;
|
||||
|
||||
virtual QmlJS::Snapshot snapshot() const = 0;
|
||||
virtual QmlJS::Snapshot newestSnapshot() const = 0;
|
||||
void updateSourceFiles(const QStringList &files,
|
||||
bool emitDocumentOnDiskChanged);
|
||||
void fileChangedOnDisk(const QString &path);
|
||||
void removeFiles(const QStringList &files);
|
||||
QStringList filesAtQrcPath(const QString &path, const QLocale *locale = 0,
|
||||
ProjectExplorer::Project *project = 0,
|
||||
QrcResourceSelector resources = AllQrcResources);
|
||||
QMap<QString,QStringList> filesInQrcPath(const QString &path,
|
||||
const QLocale *locale = 0,
|
||||
ProjectExplorer::Project *project = 0,
|
||||
bool addDirs = false,
|
||||
QrcResourceSelector resources = AllQrcResources);
|
||||
|
||||
virtual void updateSourceFiles(const QStringList &files,
|
||||
bool emitDocumentOnDiskChanged) = 0;
|
||||
virtual void fileChangedOnDisk(const QString &path) = 0;
|
||||
virtual void removeFiles(const QStringList &files) = 0;
|
||||
virtual QStringList filesAtQrcPath(const QString &path, const QLocale *locale = 0,
|
||||
ProjectExplorer::Project *project = 0,
|
||||
QrcResourceSelector resources = AllQrcResources) = 0;
|
||||
virtual QMap<QString,QStringList> filesInQrcPath(const QString &path,
|
||||
const QLocale *locale = 0,
|
||||
ProjectExplorer::Project *project = 0,
|
||||
bool addDirs = false,
|
||||
QrcResourceSelector resources = AllQrcResources) = 0;
|
||||
QList<ProjectInfo> projectInfos() const;
|
||||
ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
|
||||
void updateProjectInfo(const ProjectInfo &pinfo, ProjectExplorer::Project *p);
|
||||
|
||||
virtual QList<ProjectInfo> projectInfos() const = 0;
|
||||
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const = 0;
|
||||
virtual void updateProjectInfo(const ProjectInfo &pinfo) = 0;
|
||||
Q_SLOT virtual void removeProjectInfo(ProjectExplorer::Project *project) = 0;
|
||||
virtual ProjectInfo projectInfoForPath(QString path) = 0;
|
||||
void updateDocument(QmlJS::Document::Ptr doc);
|
||||
void updateLibraryInfo(const QString &path, const QmlJS::LibraryInfo &info);
|
||||
void emitDocumentChangedOnDisk(QmlJS::Document::Ptr doc);
|
||||
void updateQrcFile(const QString &path);
|
||||
ProjectInfo projectInfoForPath(QString path);
|
||||
|
||||
virtual QStringList importPaths() const = 0;
|
||||
virtual QmlJS::QmlLanguageBundles activeBundles() const = 0;
|
||||
virtual QmlJS::QmlLanguageBundles extendedBundles() const = 0;
|
||||
QStringList importPaths() const;
|
||||
QmlJS::QmlLanguageBundles activeBundles() const;
|
||||
QmlJS::QmlLanguageBundles extendedBundles() const;
|
||||
|
||||
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||
const QString &importUri, const QString &importVersion) = 0;
|
||||
|
||||
virtual CppDataHash cppData() const = 0;
|
||||
|
||||
virtual LibraryInfo builtins(const Document::Ptr &doc) const = 0;
|
||||
void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||
const QString &importUri, const QString &importVersion);
|
||||
|
||||
CppDataHash cppData() const;
|
||||
LibraryInfo builtins(const Document::Ptr &doc) const;
|
||||
virtual ViewerContext completeVContext(const ViewerContext &vCtx,
|
||||
const Document::Ptr &doc = Document::Ptr(0)) const = 0;
|
||||
const Document::Ptr &doc = Document::Ptr(0)) const;
|
||||
virtual ViewerContext defaultVContext(bool autoComplete = true,
|
||||
const Document::Ptr &doc = Document::Ptr(0)) const = 0;
|
||||
virtual void setDefaultVContext(const ViewerContext &vContext) = 0;
|
||||
const Document::Ptr &doc = Document::Ptr(0)) const;
|
||||
virtual void setDefaultVContext(const ViewerContext &vContext);
|
||||
|
||||
// Blocks until all parsing threads are done. Used for testing.
|
||||
virtual void joinAllThreads() = 0;
|
||||
public slots:
|
||||
virtual void resetCodeModel() = 0;
|
||||
void joinAllThreads();
|
||||
|
||||
virtual ModelManagerInterface::ProjectInfo defaultProjectInfo() const;
|
||||
|
||||
public slots:
|
||||
virtual void resetCodeModel();
|
||||
void removeProjectInfo(ProjectExplorer::Project *project);
|
||||
signals:
|
||||
void documentUpdated(QmlJS::Document::Ptr doc);
|
||||
void documentChangedOnDisk(QmlJS::Document::Ptr doc);
|
||||
void aboutToRemoveFiles(const QStringList &files);
|
||||
void libraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info);
|
||||
void projectInfoUpdated(const ProjectInfo &pinfo);
|
||||
void projectPathChanged(const QString &projectPath);
|
||||
protected slots:
|
||||
void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
|
||||
void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan);
|
||||
void asyncReset();
|
||||
virtual void startCppQmlTypeUpdate();
|
||||
protected:
|
||||
virtual QHash<QString,Language::Enum> languageForSuffix() const;
|
||||
virtual void writeMessageInternal(const QString &msg) const;
|
||||
virtual WorkingCopy workingCopyInternal() const;
|
||||
virtual void addTaskInternal(QFuture<void> result, const QString &msg, const char *taskId) const;
|
||||
|
||||
QFuture<void> refreshSourceFiles(const QStringList &sourceFiles,
|
||||
bool emitDocumentOnDiskChanged);
|
||||
|
||||
static void parseLoop(QSet<QString> &scannedPaths, QSet<QString> &newLibraries,
|
||||
WorkingCopy workingCopyInternal, QStringList files, ModelManagerInterface *modelManager,
|
||||
QmlJS::Language::Enum mainLanguage, bool emitDocChangedOnDisk,
|
||||
Utils::function<bool (qreal)> reportProgress);
|
||||
static void parse(QFutureInterface<void> &future,
|
||||
WorkingCopy workingCopyInternal,
|
||||
QStringList files,
|
||||
ModelManagerInterface *modelManager,
|
||||
QmlJS::Language::Enum mainLanguage,
|
||||
bool emitDocChangedOnDisk);
|
||||
static void importScan(QFutureInterface<void> &future,
|
||||
WorkingCopy workingCopyInternal,
|
||||
QStringList paths,
|
||||
ModelManagerInterface *modelManager,
|
||||
QmlJS::Language::Enum mainLanguage,
|
||||
bool emitDocChangedOnDisk);
|
||||
static void updateCppQmlTypes(QFutureInterface<void> &interface,
|
||||
ModelManagerInterface *qmlModelManager,
|
||||
CPlusPlus::Snapshot snapshot,
|
||||
QHash<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents);
|
||||
|
||||
void updateImportPaths();
|
||||
void loadQmlTypeDescriptionsInternal(const QString &path);
|
||||
|
||||
mutable QMutex m_mutex;
|
||||
QmlJS::Snapshot _validSnapshot;
|
||||
QmlJS::Snapshot _newestSnapshot;
|
||||
QStringList m_allImportPaths;
|
||||
QStringList m_defaultImportPaths;
|
||||
QmlJS::QmlLanguageBundles m_activeBundles;
|
||||
QmlJS::QmlLanguageBundles m_extendedBundles;
|
||||
QmlJS::ViewerContext m_vContext;
|
||||
bool m_shouldScanImports;
|
||||
QSet<QString> m_scannedPaths;
|
||||
|
||||
QTimer *m_updateCppQmlTypesTimer;
|
||||
QTimer *m_asyncResetTimer;
|
||||
QHash<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
|
||||
QFuture<void> m_cppQmlTypesUpdater;
|
||||
QrcCache m_qrcCache;
|
||||
|
||||
CppDataHash m_cppDataHash;
|
||||
mutable QMutex m_cppDataMutex;
|
||||
|
||||
// project integration
|
||||
QMap<ProjectExplorer::Project *, ProjectInfo> m_projects;
|
||||
QMultiHash<QString, ProjectExplorer::Project *> m_fileToProject;
|
||||
|
||||
PluginDumper *m_pluginDumper;
|
||||
|
||||
QFutureSynchronizer<void> m_synchronizer;
|
||||
};
|
||||
|
||||
} // namespace QmlJS
|
||||
|
||||
589
src/libs/qmljs/qmljsplugindumper.cpp
Normal file
589
src/libs/qmljs/qmljsplugindumper.cpp
Normal file
@@ -0,0 +1,589 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmljsplugindumper.h"
|
||||
#include "qmljsmodelmanagerinterface.h"
|
||||
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
//#include <projectexplorer/session.h>
|
||||
//#include <coreplugin/messagemanager.h>
|
||||
#include <utils/filesystemwatcher.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
using namespace LanguageUtils;
|
||||
using namespace QmlJS;
|
||||
|
||||
PluginDumper::PluginDumper(ModelManagerInterface *modelManager)
|
||||
: QObject(modelManager)
|
||||
, m_modelManager(modelManager)
|
||||
, m_pluginWatcher(0)
|
||||
{
|
||||
qRegisterMetaType<QmlJS::ModelManagerInterface::ProjectInfo>("QmlJS::ModelManagerInterface::ProjectInfo");
|
||||
}
|
||||
|
||||
Utils::FileSystemWatcher *PluginDumper::pluginWatcher()
|
||||
{
|
||||
if (!m_pluginWatcher) {
|
||||
m_pluginWatcher = new Utils::FileSystemWatcher(this);
|
||||
m_pluginWatcher->setObjectName(QLatin1String("PluginDumperWatcher"));
|
||||
connect(m_pluginWatcher, SIGNAL(fileChanged(QString)),
|
||||
this, SLOT(pluginChanged(QString)));
|
||||
}
|
||||
return m_pluginWatcher;
|
||||
}
|
||||
|
||||
void PluginDumper::loadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info)
|
||||
{
|
||||
// move to the owning thread
|
||||
metaObject()->invokeMethod(this, "onLoadBuiltinTypes",
|
||||
Q_ARG(QmlJS::ModelManagerInterface::ProjectInfo, info));
|
||||
}
|
||||
|
||||
void PluginDumper::loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion)
|
||||
{
|
||||
// move to the owning thread
|
||||
metaObject()->invokeMethod(this, "onLoadPluginTypes",
|
||||
Q_ARG(QString, libraryPath),
|
||||
Q_ARG(QString, importPath),
|
||||
Q_ARG(QString, importUri),
|
||||
Q_ARG(QString, importVersion));
|
||||
}
|
||||
|
||||
void PluginDumper::scheduleRedumpPlugins()
|
||||
{
|
||||
// move to the owning thread
|
||||
metaObject()->invokeMethod(this, "dumpAllPlugins", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void PluginDumper::scheduleMaybeRedumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info)
|
||||
{
|
||||
// move to the owning thread
|
||||
metaObject()->invokeMethod(this, "dumpBuiltins", Qt::QueuedConnection,
|
||||
Q_ARG(QmlJS::ModelManagerInterface::ProjectInfo, info));
|
||||
}
|
||||
|
||||
void PluginDumper::onLoadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info, bool force)
|
||||
{
|
||||
if (info.qmlDumpPath.isEmpty() || info.qtImportsPath.isEmpty())
|
||||
return;
|
||||
|
||||
const QString importsPath = QDir::cleanPath(info.qtImportsPath);
|
||||
if (m_runningQmldumps.values().contains(importsPath))
|
||||
return;
|
||||
|
||||
LibraryInfo builtinInfo;
|
||||
if (!force) {
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
builtinInfo = snapshot.libraryInfo(info.qtImportsPath);
|
||||
if (builtinInfo.isValid())
|
||||
return;
|
||||
}
|
||||
builtinInfo = LibraryInfo(LibraryInfo::Found);
|
||||
m_modelManager->updateLibraryInfo(info.qtImportsPath, builtinInfo);
|
||||
|
||||
// prefer QTDIR/imports/builtins.qmltypes if available
|
||||
const QString builtinQmltypesPath = info.qtImportsPath + QLatin1String("/builtins.qmltypes");
|
||||
if (QFile::exists(builtinQmltypesPath)) {
|
||||
loadQmltypesFile(QStringList(builtinQmltypesPath), info.qtImportsPath, builtinInfo);
|
||||
return;
|
||||
}
|
||||
// QTDIR/imports/QtQuick1/builtins.qmltypes was used in developer builds of 5.0.0, 5.0.1
|
||||
const QString builtinQmltypesPath2 = info.qtImportsPath
|
||||
+ QLatin1String("/QtQuick1/builtins.qmltypes");
|
||||
if (QFile::exists(builtinQmltypesPath2)) {
|
||||
loadQmltypesFile(QStringList(builtinQmltypesPath2), info.qtImportsPath, builtinInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// run qmldump
|
||||
QProcess *process = new QProcess(this);
|
||||
process->setEnvironment(info.qmlDumpEnvironment.toStringList());
|
||||
connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int)));
|
||||
connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(qmlPluginTypeDumpError(QProcess::ProcessError)));
|
||||
QStringList args(QLatin1String("--builtins"));
|
||||
process->start(info.qmlDumpPath, args);
|
||||
m_runningQmldumps.insert(process, info.qtImportsPath);
|
||||
m_qtToInfo.insert(info.qtImportsPath, info);
|
||||
}
|
||||
|
||||
static QString makeAbsolute(const QString &path, const QString &base)
|
||||
{
|
||||
if (QFileInfo(path).isAbsolute())
|
||||
return path;
|
||||
return QString::fromLatin1("%1%2%3").arg(base, QDir::separator(), path);
|
||||
}
|
||||
|
||||
void PluginDumper::onLoadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri, const QString &importVersion)
|
||||
{
|
||||
const QString canonicalLibraryPath = QDir::cleanPath(libraryPath);
|
||||
if (m_runningQmldumps.values().contains(canonicalLibraryPath))
|
||||
return;
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
const LibraryInfo libraryInfo = snapshot.libraryInfo(canonicalLibraryPath);
|
||||
if (libraryInfo.pluginTypeInfoStatus() != LibraryInfo::NoTypeInfo)
|
||||
return;
|
||||
|
||||
// avoid inserting the same plugin twice
|
||||
int index;
|
||||
for (index = 0; index < m_plugins.size(); ++index) {
|
||||
if (m_plugins.at(index).qmldirPath == libraryPath)
|
||||
break;
|
||||
}
|
||||
if (index == m_plugins.size())
|
||||
m_plugins.append(Plugin());
|
||||
|
||||
Plugin &plugin = m_plugins[index];
|
||||
plugin.qmldirPath = canonicalLibraryPath;
|
||||
plugin.importPath = importPath;
|
||||
plugin.importUri = importUri;
|
||||
plugin.importVersion = importVersion;
|
||||
|
||||
// add default qmltypes file if it exists
|
||||
const QLatin1String defaultQmltypesFileName("plugins.qmltypes");
|
||||
const QString defaultQmltypesPath = makeAbsolute(defaultQmltypesFileName, canonicalLibraryPath);
|
||||
if (!plugin.typeInfoPaths.contains(defaultQmltypesPath) && QFile::exists(defaultQmltypesPath))
|
||||
plugin.typeInfoPaths += defaultQmltypesPath;
|
||||
|
||||
// add typeinfo files listed in qmldir
|
||||
foreach (const QmlDirParser::TypeInfo &typeInfo, libraryInfo.typeInfos()) {
|
||||
QString pathNow = makeAbsolute(typeInfo.fileName, canonicalLibraryPath);
|
||||
if (!plugin.typeInfoPaths.contains(pathNow) && QFile::exists(pathNow))
|
||||
plugin.typeInfoPaths += pathNow;
|
||||
}
|
||||
|
||||
// watch plugin libraries
|
||||
foreach (const QmlDirParser::Plugin &plugin, snapshot.libraryInfo(canonicalLibraryPath).plugins()) {
|
||||
const QString pluginLibrary = resolvePlugin(canonicalLibraryPath, plugin.path, plugin.name);
|
||||
if (!pluginLibrary.isEmpty()) {
|
||||
if (!pluginWatcher()->watchesFile(pluginLibrary))
|
||||
pluginWatcher()->addFile(pluginLibrary, Utils::FileSystemWatcher::WatchModifiedDate);
|
||||
m_libraryToPluginIndex.insert(pluginLibrary, index);
|
||||
}
|
||||
}
|
||||
|
||||
// watch library qmltypes file
|
||||
if (!plugin.typeInfoPaths.isEmpty()) {
|
||||
foreach (const QString &path, plugin.typeInfoPaths) {
|
||||
if (!QFile::exists(path))
|
||||
continue;
|
||||
if (!pluginWatcher()->watchesFile(path))
|
||||
pluginWatcher()->addFile(path, Utils::FileSystemWatcher::WatchModifiedDate);
|
||||
m_libraryToPluginIndex.insert(path, index);
|
||||
}
|
||||
}
|
||||
|
||||
dump(plugin);
|
||||
}
|
||||
|
||||
void PluginDumper::dumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info)
|
||||
{
|
||||
// if the builtin types were generated with a different qmldump, regenerate!
|
||||
if (m_qtToInfo.contains(info.qtImportsPath)) {
|
||||
QmlJS::ModelManagerInterface::ProjectInfo oldInfo = m_qtToInfo.value(info.qtImportsPath);
|
||||
if (oldInfo.qmlDumpPath != info.qmlDumpPath
|
||||
|| oldInfo.qmlDumpEnvironment != info.qmlDumpEnvironment) {
|
||||
m_qtToInfo.remove(info.qtImportsPath);
|
||||
onLoadBuiltinTypes(info, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginDumper::dumpAllPlugins()
|
||||
{
|
||||
foreach (const Plugin &plugin, m_plugins) {
|
||||
dump(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
static QString noTypeinfoError(const QString &libraryPath)
|
||||
{
|
||||
return PluginDumper::tr("QML module does not contain information about components contained in plugins\n\n"
|
||||
"Module path: %1\n"
|
||||
"See \"Using QML Modules with Plugins\" in the documentation.").arg(
|
||||
libraryPath);
|
||||
}
|
||||
|
||||
static QString qmldumpErrorMessage(const QString &libraryPath, const QString &error)
|
||||
{
|
||||
return noTypeinfoError(libraryPath) + QLatin1String("\n\n") +
|
||||
PluginDumper::tr("Automatic type dump of QML module failed.\nErrors:\n%1").
|
||||
arg(error) + QLatin1Char('\n');
|
||||
}
|
||||
|
||||
static QString qmldumpFailedMessage(const QString &libraryPath, const QString &error)
|
||||
{
|
||||
QString firstLines =
|
||||
QStringList(error.split(QLatin1Char('\n')).mid(0, 10)).join(QLatin1String("\n"));
|
||||
return noTypeinfoError(libraryPath) + QLatin1String("\n\n") +
|
||||
PluginDumper::tr("Automatic type dump of QML module failed.\n"
|
||||
"First 10 lines or errors:\n"
|
||||
"\n"
|
||||
"%1"
|
||||
"\n"
|
||||
"Check 'General Messages' output pane for details."
|
||||
).arg(firstLines);
|
||||
}
|
||||
|
||||
static void printParseWarnings(const QString &libraryPath, const QString &warning)
|
||||
{
|
||||
ModelManagerInterface::writeWarning(
|
||||
PluginDumper::tr("Warnings while parsing qmltypes information of %1:\n"
|
||||
"%2").arg(libraryPath, warning));
|
||||
}
|
||||
|
||||
static QString qmlPluginDumpErrorMessage(QProcess *process)
|
||||
{
|
||||
QString errorMessage;
|
||||
#if QT_VERSION >= 0x050000
|
||||
const QString binary = QDir::toNativeSeparators(process->program());
|
||||
#else
|
||||
const QString binary = QLatin1String("qmlplugindump");
|
||||
#endif
|
||||
switch (process->error()) {
|
||||
case QProcess::FailedToStart:
|
||||
errorMessage = PluginDumper::tr("\"%1\" failed to start: %2").arg(binary, process->errorString());
|
||||
break;
|
||||
case QProcess::Crashed:
|
||||
errorMessage = PluginDumper::tr("\"%1\" crashed.").arg(binary);
|
||||
break;
|
||||
case QProcess::Timedout:
|
||||
errorMessage = PluginDumper::tr("\"%1\" timed out.").arg(binary);
|
||||
break;
|
||||
case QProcess::ReadError:
|
||||
case QProcess::WriteError:
|
||||
errorMessage = PluginDumper::tr("I/O error running \"%1\".").arg(binary);
|
||||
break;
|
||||
case QProcess::UnknownError:
|
||||
if (process->exitCode())
|
||||
errorMessage = PluginDumper::tr("\"%1\" returned exit code %2.").arg(binary).arg(process->exitCode());
|
||||
break;
|
||||
}
|
||||
#if QT_VERSION >= 0x050000
|
||||
errorMessage += QLatin1Char('\n') + PluginDumper::tr("Arguments: %1").arg(process->arguments().join(QLatin1Char(' ')));
|
||||
#endif
|
||||
if (process->error() != QProcess::FailedToStart) {
|
||||
const QString stdErr = QString::fromLocal8Bit(process->readAllStandardError());
|
||||
if (!stdErr.isEmpty()) {
|
||||
errorMessage += QLatin1Char('\n');
|
||||
errorMessage += stdErr;
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
|
||||
{
|
||||
QProcess *process = qobject_cast<QProcess *>(sender());
|
||||
if (!process)
|
||||
return;
|
||||
process->deleteLater();
|
||||
|
||||
const QString libraryPath = m_runningQmldumps.take(process);
|
||||
if (libraryPath.isEmpty())
|
||||
return;
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
|
||||
|
||||
if (exitCode != 0) {
|
||||
const QString errorMessages = qmlPluginDumpErrorMessage(process);
|
||||
ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
|
||||
}
|
||||
|
||||
const QByteArray output = process->readAllStandardOutput();
|
||||
QString error;
|
||||
QString warning;
|
||||
CppQmlTypesLoader::BuiltinObjects objectsList;
|
||||
QList<ModuleApiInfo> moduleApis;
|
||||
CppQmlTypesLoader::parseQmlTypeDescriptions(output, &objectsList, &moduleApis, &error, &warning,
|
||||
QLatin1String("<dump of ") + libraryPath + QLatin1String(">"));
|
||||
if (exitCode == 0) {
|
||||
if (!error.isEmpty()) {
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
|
||||
qmldumpErrorMessage(libraryPath, error));
|
||||
printParseWarnings(libraryPath, libraryInfo.pluginTypeInfoError());
|
||||
} else {
|
||||
libraryInfo.setMetaObjects(objectsList.values());
|
||||
libraryInfo.setModuleApis(moduleApis);
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
||||
}
|
||||
|
||||
if (!warning.isEmpty())
|
||||
printParseWarnings(libraryPath, warning);
|
||||
}
|
||||
libraryInfo.updateFingerprint();
|
||||
|
||||
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
||||
}
|
||||
|
||||
void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
|
||||
{
|
||||
QProcess *process = qobject_cast<QProcess *>(sender());
|
||||
if (!process)
|
||||
return;
|
||||
process->deleteLater();
|
||||
|
||||
const QString libraryPath = m_runningQmldumps.take(process);
|
||||
if (libraryPath.isEmpty())
|
||||
return;
|
||||
|
||||
const QString errorMessages = qmlPluginDumpErrorMessage(process);
|
||||
ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
|
||||
if (!libraryPath.isEmpty()) {
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
|
||||
libraryInfo.updateFingerprint();
|
||||
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginDumper::pluginChanged(const QString &pluginLibrary)
|
||||
{
|
||||
const int pluginIndex = m_libraryToPluginIndex.value(pluginLibrary, -1);
|
||||
if (pluginIndex == -1)
|
||||
return;
|
||||
|
||||
const Plugin &plugin = m_plugins.at(pluginIndex);
|
||||
dump(plugin);
|
||||
}
|
||||
|
||||
void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
||||
const QString &libraryPath,
|
||||
QmlJS::LibraryInfo libraryInfo)
|
||||
{
|
||||
QStringList errors;
|
||||
QStringList warnings;
|
||||
QList<FakeMetaObject::ConstPtr> objects;
|
||||
QList<ModuleApiInfo> moduleApis;
|
||||
|
||||
foreach (const QString &qmltypesFilePath, qmltypesFilePaths) {
|
||||
Utils::FileReader reader;
|
||||
if (!reader.fetch(qmltypesFilePath, QFile::Text)) {
|
||||
errors += reader.errorString();
|
||||
continue;
|
||||
}
|
||||
|
||||
QString error;
|
||||
QString warning;
|
||||
CppQmlTypesLoader::BuiltinObjects newObjects;
|
||||
QList<ModuleApiInfo> newModuleApis;
|
||||
CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &newObjects, &newModuleApis, &error, &warning, qmltypesFilePath);
|
||||
if (!error.isEmpty()) {
|
||||
errors += tr("Failed to parse '%1'.\nError: %2").arg(qmltypesFilePath, error);
|
||||
} else {
|
||||
objects += newObjects.values();
|
||||
moduleApis += newModuleApis;
|
||||
}
|
||||
if (!warning.isEmpty())
|
||||
warnings += warning;
|
||||
}
|
||||
|
||||
libraryInfo.setMetaObjects(objects);
|
||||
libraryInfo.setModuleApis(moduleApis);
|
||||
if (errors.isEmpty()) {
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
|
||||
} else {
|
||||
printParseWarnings(libraryPath, errors.join(QLatin1String("\n")));
|
||||
errors.prepend(tr("Errors while reading typeinfo files:"));
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1String("\n")));
|
||||
}
|
||||
|
||||
if (!warnings.isEmpty())
|
||||
printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
|
||||
|
||||
libraryInfo.updateFingerprint();
|
||||
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
|
||||
}
|
||||
|
||||
void PluginDumper::dump(const Plugin &plugin)
|
||||
{
|
||||
// if there are type infos, don't dump!
|
||||
if (!plugin.typeInfoPaths.isEmpty()) {
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
LibraryInfo libraryInfo = snapshot.libraryInfo(plugin.qmldirPath);
|
||||
if (!libraryInfo.isValid())
|
||||
return;
|
||||
|
||||
loadQmltypesFile(plugin.typeInfoPaths, plugin.qmldirPath, libraryInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
ModelManagerInterface::ProjectInfo info = m_modelManager->defaultProjectInfo();
|
||||
|
||||
if (!info.tryQmlDump || info.qmlDumpPath.isEmpty()) {
|
||||
const Snapshot snapshot = m_modelManager->snapshot();
|
||||
LibraryInfo libraryInfo = snapshot.libraryInfo(plugin.qmldirPath);
|
||||
if (!libraryInfo.isValid())
|
||||
return;
|
||||
|
||||
QString errorMessage;
|
||||
if (!info.tryQmlDump) {
|
||||
errorMessage = noTypeinfoError(plugin.qmldirPath);
|
||||
} else {
|
||||
errorMessage = qmldumpErrorMessage(plugin.qmldirPath,
|
||||
tr("Could not locate the helper application for dumping type information from C++ plugins.\n"
|
||||
"Please build the qmldump application on the Qt version options page."));
|
||||
}
|
||||
|
||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, errorMessage);
|
||||
libraryInfo.updateFingerprint();
|
||||
m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
QProcess *process = new QProcess(this);
|
||||
process->setEnvironment(info.qmlDumpEnvironment.toStringList());
|
||||
connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int)));
|
||||
connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(qmlPluginTypeDumpError(QProcess::ProcessError)));
|
||||
QStringList args;
|
||||
if (plugin.importUri.isEmpty()) {
|
||||
args << QLatin1String("--path");
|
||||
args << plugin.importPath;
|
||||
if (ComponentVersion(plugin.importVersion).isValid())
|
||||
args << plugin.importVersion;
|
||||
} else {
|
||||
if (info.qmlDumpHasRelocatableFlag)
|
||||
args << QLatin1String("-relocatable");
|
||||
args << plugin.importUri;
|
||||
args << plugin.importVersion;
|
||||
args << plugin.importPath;
|
||||
}
|
||||
process->start(info.qmlDumpPath, args);
|
||||
m_runningQmldumps.insert(process, plugin.qmldirPath);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
|
||||
The \a prefix must contain the dot.
|
||||
|
||||
\a qmldirPath is the location of the qmldir file.
|
||||
|
||||
Adapted from QDeclarativeImportDatabase::resolvePlugin.
|
||||
*/
|
||||
QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
|
||||
const QString &baseName, const QStringList &suffixes,
|
||||
const QString &prefix)
|
||||
{
|
||||
QStringList searchPaths;
|
||||
searchPaths.append(QLatin1String("."));
|
||||
|
||||
bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
|
||||
if (!qmldirPluginPathIsRelative)
|
||||
searchPaths.prepend(qmldirPluginPath);
|
||||
|
||||
foreach (const QString &pluginPath, searchPaths) {
|
||||
|
||||
QString resolvedPath;
|
||||
|
||||
if (pluginPath == QLatin1String(".")) {
|
||||
if (qmldirPluginPathIsRelative)
|
||||
resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
|
||||
else
|
||||
resolvedPath = qmldirPath.absolutePath();
|
||||
} else {
|
||||
resolvedPath = pluginPath;
|
||||
}
|
||||
|
||||
QDir dir(resolvedPath);
|
||||
foreach (const QString &suffix, suffixes) {
|
||||
QString pluginFileName = prefix;
|
||||
|
||||
pluginFileName += baseName;
|
||||
pluginFileName += suffix;
|
||||
|
||||
QFileInfo fileInfo(dir, pluginFileName);
|
||||
|
||||
if (fileInfo.exists())
|
||||
return fileInfo.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the result of the merge of \a baseName with \a dir and the platform suffix.
|
||||
|
||||
Adapted from QDeclarativeImportDatabase::resolvePlugin.
|
||||
|
||||
\table
|
||||
\header \li Platform \li Valid suffixes
|
||||
\row \li Windows \li \c .dll
|
||||
\row \li Unix/Linux \li \c .so
|
||||
\row \li AIX \li \c .a
|
||||
\row \li HP-UX \li \c .sl, \c .so (HP-UXi)
|
||||
\row \li Mac OS X \li \c .dylib, \c .bundle, \c .so
|
||||
\endtable
|
||||
|
||||
Version number on unix are ignored.
|
||||
*/
|
||||
QString PluginDumper::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
|
||||
const QString &baseName)
|
||||
{
|
||||
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
|
||||
return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
|
||||
QStringList()
|
||||
<< QLatin1String("d.dll") // try a qmake-style debug build first
|
||||
<< QLatin1String(".dll"));
|
||||
#elif defined(Q_OS_DARWIN)
|
||||
return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
|
||||
QStringList()
|
||||
<< QLatin1String("_debug.dylib") // try a qmake-style debug build first
|
||||
<< QLatin1String(".dylib")
|
||||
<< QLatin1String(".so")
|
||||
<< QLatin1String(".bundle"),
|
||||
QLatin1String("lib"));
|
||||
#else // Generic Unix
|
||||
QStringList validSuffixList;
|
||||
|
||||
# if defined(Q_OS_HPUX)
|
||||
/*
|
||||
See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
|
||||
"In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
|
||||
the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
|
||||
*/
|
||||
validSuffixList << QLatin1String(".sl");
|
||||
# if defined __ia64
|
||||
validSuffixList << QLatin1String(".so");
|
||||
# endif
|
||||
# elif defined(Q_OS_AIX)
|
||||
validSuffixList << QLatin1String(".a") << QLatin1String(".so");
|
||||
# elif defined(Q_OS_UNIX)
|
||||
validSuffixList << QLatin1String(".so");
|
||||
# endif
|
||||
|
||||
// Examples of valid library names:
|
||||
// libfoo.so
|
||||
|
||||
return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
|
||||
#endif
|
||||
}
|
||||
104
src/libs/qmljs/qmljsplugindumper.h
Normal file
104
src/libs/qmljs/qmljsplugindumper.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLJSPLUGINDUMPER_H
|
||||
#define QMLJSPLUGINDUMPER_H
|
||||
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QProcess>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDir;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils { class FileSystemWatcher; }
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class PluginDumper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PluginDumper(ModelManagerInterface *modelManager);
|
||||
|
||||
public:
|
||||
void loadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info);
|
||||
void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||
const QString &importUri, const QString &importVersion);
|
||||
void scheduleRedumpPlugins();
|
||||
void scheduleMaybeRedumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info);
|
||||
|
||||
private slots:
|
||||
void onLoadBuiltinTypes(const QmlJS::ModelManagerInterface::ProjectInfo &info,
|
||||
bool force = false);
|
||||
void onLoadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||
const QString &importUri, const QString &importVersion);
|
||||
void dumpBuiltins(const QmlJS::ModelManagerInterface::ProjectInfo &info);
|
||||
void dumpAllPlugins();
|
||||
void qmlPluginTypeDumpDone(int exitCode);
|
||||
void qmlPluginTypeDumpError(QProcess::ProcessError error);
|
||||
void pluginChanged(const QString &pluginLibrary);
|
||||
|
||||
private:
|
||||
class Plugin {
|
||||
public:
|
||||
QString qmldirPath;
|
||||
QString importPath;
|
||||
QString importUri;
|
||||
QString importVersion;
|
||||
QStringList typeInfoPaths;
|
||||
};
|
||||
|
||||
void dump(const Plugin &plugin);
|
||||
void loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
||||
const QString &libraryPath,
|
||||
QmlJS::LibraryInfo libraryInfo);
|
||||
QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
|
||||
const QString &baseName);
|
||||
QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
|
||||
const QString &baseName, const QStringList &suffixes,
|
||||
const QString &prefix = QString());
|
||||
|
||||
private:
|
||||
Utils::FileSystemWatcher *pluginWatcher();
|
||||
|
||||
ModelManagerInterface *m_modelManager;
|
||||
Utils::FileSystemWatcher *m_pluginWatcher;
|
||||
QHash<QProcess *, QString> m_runningQmldumps;
|
||||
QList<Plugin> m_plugins;
|
||||
QHash<QString, int> m_libraryToPluginIndex;
|
||||
QHash<QString, QmlJS::ModelManagerInterface::ProjectInfo> m_qtToInfo;
|
||||
};
|
||||
|
||||
} // namespace QmlJS
|
||||
|
||||
#endif // QMLJSPLUGINDUMPER_H
|
||||
Reference in New Issue
Block a user