forked from qt-creator/qt-creator
QmlJS: Find setContextProperty calls in C++ and expose to QML.
Task-number: QTCREATORBUG-3199 Change-Id: I591490ceafadc0f5a07c63ec063f1bdfa7055f47 Reviewed-on: http://codereview.qt-project.org/4074 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),
|
||||||
|
_checkMode(0)
|
||||||
{
|
{
|
||||||
_control = new Control();
|
_control = new Control();
|
||||||
|
|
||||||
@@ -569,6 +570,8 @@ void Document::check(CheckMode mode)
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!_globalNamespace);
|
Q_ASSERT(!_globalNamespace);
|
||||||
|
|
||||||
|
_checkMode = mode;
|
||||||
|
|
||||||
if (! isParsed())
|
if (! isParsed())
|
||||||
parse();
|
parse();
|
||||||
|
|
||||||
|
@@ -120,6 +120,7 @@ public:
|
|||||||
bool parse(ParseMode mode = ParseTranlationUnit);
|
bool parse(ParseMode mode = ParseTranlationUnit);
|
||||||
|
|
||||||
enum CheckMode {
|
enum CheckMode {
|
||||||
|
Unchecked,
|
||||||
FullCheck,
|
FullCheck,
|
||||||
FastCheck
|
FastCheck
|
||||||
};
|
};
|
||||||
@@ -322,6 +323,9 @@ public:
|
|||||||
void keepSourceAndAST();
|
void keepSourceAndAST();
|
||||||
void releaseSourceAndAST();
|
void releaseSourceAndAST();
|
||||||
|
|
||||||
|
CheckMode checkMode() const
|
||||||
|
{ return static_cast<CheckMode>(_checkMode); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _fileName;
|
QString _fileName;
|
||||||
Control *_control;
|
Control *_control;
|
||||||
@@ -338,6 +342,7 @@ private:
|
|||||||
QAtomicInt _keepSourceAndASTCount;
|
QAtomicInt _keepSourceAndASTCount;
|
||||||
unsigned _revision;
|
unsigned _revision;
|
||||||
unsigned _editorRevision;
|
unsigned _editorRevision;
|
||||||
|
quint8 _checkMode;
|
||||||
|
|
||||||
friend class Snapshot;
|
friend class Snapshot;
|
||||||
};
|
};
|
||||||
|
@@ -120,11 +120,29 @@ Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const Libra
|
|||||||
d->diagnosticMessages = 0;
|
d->diagnosticMessages = 0;
|
||||||
d->allDiagnosticMessages = 0;
|
d->allDiagnosticMessages = 0;
|
||||||
|
|
||||||
// populate engine with types from C++
|
|
||||||
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
||||||
if (modelManager) {
|
if (modelManager) {
|
||||||
foreach (const QList<FakeMetaObject::ConstPtr> &cppTypes, modelManager->cppQmlTypes()) {
|
ModelManagerInterface::CppDataHash cppDataHash = modelManager->cppData();
|
||||||
d->valueOwner->cppQmlTypes().load(d->valueOwner, cppTypes);
|
|
||||||
|
// populate engine with types from C++
|
||||||
|
foreach (const ModelManagerInterface::CppData &cppData, cppDataHash) {
|
||||||
|
d->valueOwner->cppQmlTypes().load(d->valueOwner, cppData.exportedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// populate global object with context properties from C++
|
||||||
|
ObjectValue *global = d->valueOwner->globalObject();
|
||||||
|
foreach (const ModelManagerInterface::CppData &cppData, cppDataHash) {
|
||||||
|
QMapIterator<QString, QString> it(cppData.contextProperties);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
const Value *value = 0;
|
||||||
|
const QString cppTypeName = it.value();
|
||||||
|
if (!cppTypeName.isEmpty())
|
||||||
|
value = d->valueOwner->cppQmlTypes().typeByCppName(cppTypeName);
|
||||||
|
if (!value)
|
||||||
|
value = d->valueOwner->undefinedValue();
|
||||||
|
global->setMember(it.key(), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -110,7 +110,14 @@ public:
|
|||||||
Table _elements;
|
Table _elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QHash<QString, QList<LanguageUtils::FakeMetaObject::ConstPtr> > CppQmlTypeHash;
|
class CppData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedTypes;
|
||||||
|
QMap<QString, QString> contextProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<QString, CppData> CppDataHash;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModelManagerInterface(QObject *parent = 0);
|
ModelManagerInterface(QObject *parent = 0);
|
||||||
@@ -138,7 +145,7 @@ public:
|
|||||||
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||||
const QString &importUri, const QString &importVersion) = 0;
|
const QString &importUri, const QString &importVersion) = 0;
|
||||||
|
|
||||||
virtual CppQmlTypeHash cppQmlTypes() const = 0;
|
virtual CppDataHash cppData() const = 0;
|
||||||
|
|
||||||
virtual LibraryInfo builtins(const Document::Ptr &doc) const = 0;
|
virtual LibraryInfo builtins(const Document::Ptr &doc) const = 0;
|
||||||
|
|
||||||
|
@@ -64,10 +64,18 @@ public:
|
|||||||
QString typeExpression;
|
QString typeExpression;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ContextProperty {
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString expression;
|
||||||
|
unsigned line, column;
|
||||||
|
};
|
||||||
|
|
||||||
class FindExportsVisitor : protected ASTVisitor
|
class FindExportsVisitor : protected ASTVisitor
|
||||||
{
|
{
|
||||||
CPlusPlus::Document::Ptr _doc;
|
CPlusPlus::Document::Ptr _doc;
|
||||||
QList<ExportedQmlType> _exportedTypes;
|
QList<ExportedQmlType> _exportedTypes;
|
||||||
|
QList<ContextProperty> _contextProperties;
|
||||||
CompoundStatementAST *_compound;
|
CompoundStatementAST *_compound;
|
||||||
ASTMatcher _matcher;
|
ASTMatcher _matcher;
|
||||||
ASTPatternBuilder _builder;
|
ASTPatternBuilder _builder;
|
||||||
@@ -81,11 +89,11 @@ public:
|
|||||||
, _compound(0)
|
, _compound(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QList<ExportedQmlType> operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
_exportedTypes.clear();
|
_exportedTypes.clear();
|
||||||
|
_contextProperties.clear();
|
||||||
accept(translationUnit()->ast());
|
accept(translationUnit()->ast());
|
||||||
return _exportedTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Document::DiagnosticMessage> messages() const
|
QList<Document::DiagnosticMessage> messages() const
|
||||||
@@ -93,6 +101,16 @@ public:
|
|||||||
return _messages;
|
return _messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ExportedQmlType> exportedTypes() const
|
||||||
|
{
|
||||||
|
return _exportedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ContextProperty> contextProperties() const
|
||||||
|
{
|
||||||
|
return _contextProperties;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool visit(CompoundStatementAST *ast)
|
virtual bool visit(CompoundStatementAST *ast)
|
||||||
{
|
{
|
||||||
@@ -104,6 +122,14 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual bool visit(CallAST *ast)
|
virtual bool visit(CallAST *ast)
|
||||||
|
{
|
||||||
|
if (checkForQmlRegisterType(ast))
|
||||||
|
return false;
|
||||||
|
checkForSetContextProperty(ast);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkForQmlRegisterType(CallAST *ast)
|
||||||
{
|
{
|
||||||
IdExpressionAST *idExp = ast->base_expression->asIdExpression();
|
IdExpressionAST *idExp = ast->base_expression->asIdExpression();
|
||||||
if (!idExp || !idExp->name)
|
if (!idExp || !idExp->name)
|
||||||
@@ -139,7 +165,7 @@ protected:
|
|||||||
|
|
||||||
// last argument must be a string literal
|
// last argument must be a string literal
|
||||||
const StringLiteral *nameLit = 0;
|
const StringLiteral *nameLit = 0;
|
||||||
if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
|
if (StringLiteralAST *nameAst = skipStringCall(ast->expression_list->next->next->next->value)->asStringLiteral())
|
||||||
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
||||||
if (!nameLit) {
|
if (!nameLit) {
|
||||||
unsigned line, column;
|
unsigned line, column;
|
||||||
@@ -155,7 +181,7 @@ protected:
|
|||||||
|
|
||||||
// if the first argument is a string literal, things are easy
|
// if the first argument is a string literal, things are easy
|
||||||
QString packageName;
|
QString packageName;
|
||||||
if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
|
if (StringLiteralAST *packageAst = skipStringCall(ast->expression_list->value)->asStringLiteral()) {
|
||||||
const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
|
const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
|
||||||
packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
|
packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
|
||||||
}
|
}
|
||||||
@@ -242,7 +268,118 @@ protected:
|
|||||||
|
|
||||||
_exportedTypes += exportedType;
|
_exportedTypes += exportedType;
|
||||||
|
|
||||||
return false;
|
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,
|
||||||
|
FindExportedCppTypes::tr(
|
||||||
|
"must be a string literal to be available in 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:
|
private:
|
||||||
@@ -265,8 +402,8 @@ private:
|
|||||||
|
|
||||||
ExpressionAST *skipStringCall(ExpressionAST *exp)
|
ExpressionAST *skipStringCall(ExpressionAST *exp)
|
||||||
{
|
{
|
||||||
if (!exp)
|
if (!exp || !exp->asCall())
|
||||||
return 0;
|
return exp;
|
||||||
|
|
||||||
IdExpressionAST *callName = _builder.IdExpression();
|
IdExpressionAST *callName = _builder.IdExpression();
|
||||||
CallAST *call = _builder.Call(callName);
|
CallAST *call = _builder.Call(callName);
|
||||||
@@ -381,15 +518,26 @@ static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpress
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
|
static LanguageUtils::FakeMetaObject::Ptr buildFakeMetaObject(
|
||||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
|
Class *klass,
|
||||||
TypeOfExpression &typeOf)
|
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects,
|
||||||
|
TypeOfExpression &typeOf)
|
||||||
{
|
{
|
||||||
using namespace LanguageUtils;
|
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;
|
Overview namePrinter;
|
||||||
|
|
||||||
classes->insert(klass, fmo);
|
fmo->setClassName(namePrinter(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) {
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
||||||
Symbol *member = klass->memberAt(i);
|
Symbol *member = klass->memberAt(i);
|
||||||
@@ -454,56 +602,74 @@ static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
|
|||||||
if (klass->baseClassCount() > 0) {
|
if (klass->baseClassCount() > 0) {
|
||||||
BaseClass *base = klass->baseClassAt(0);
|
BaseClass *base = klass->baseClassAt(0);
|
||||||
if (!base->name())
|
if (!base->name())
|
||||||
return;
|
return fmo;
|
||||||
|
|
||||||
const QString baseClassName = namePrinter(base->name());
|
const QString baseClassName = namePrinter(base->name());
|
||||||
fmo->setSuperclassName(baseClassName);
|
fmo->setSuperclassName(baseClassName);
|
||||||
|
|
||||||
Class *baseClass = lookupClass(baseClassName, klass, typeOf);
|
Class *baseClass = lookupClass(baseClassName, klass, typeOf);
|
||||||
if (!baseClass)
|
if (!baseClass)
|
||||||
return;
|
return fmo;
|
||||||
|
|
||||||
FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
|
buildFakeMetaObject(baseClass, fakeMetaObjects, typeOf);
|
||||||
if (!baseFmo) {
|
|
||||||
baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
|
|
||||||
populate(baseFmo, baseClass, classes, typeOf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fmo;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(
|
static void buildExportedQmlObjects(
|
||||||
const Document::Ptr &doc,
|
TypeOfExpression &typeOf,
|
||||||
const Snapshot &snapshot,
|
const QList<ExportedQmlType> &cppExports,
|
||||||
const QList<ExportedQmlType> &exportedTypes)
|
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects)
|
||||||
{
|
{
|
||||||
using namespace LanguageUtils;
|
using namespace LanguageUtils;
|
||||||
QList<FakeMetaObject::ConstPtr> exportedObjects;
|
|
||||||
QHash<Class *, FakeMetaObject::Ptr> classes;
|
|
||||||
|
|
||||||
if (exportedTypes.isEmpty())
|
if (cppExports.isEmpty())
|
||||||
return exportedObjects;
|
return;
|
||||||
|
|
||||||
TypeOfExpression typeOf;
|
foreach (const ExportedQmlType &exportedType, cppExports) {
|
||||||
typeOf.init(doc, snapshot);
|
Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
|
||||||
foreach (const ExportedQmlType &exportedType, exportedTypes) {
|
// accepts a null klass
|
||||||
FakeMetaObject::Ptr fmo(new FakeMetaObject);
|
FakeMetaObject::Ptr fmo = buildFakeMetaObject(klass, fakeMetaObjects, typeOf);
|
||||||
fmo->addExport(exportedType.typeName,
|
fmo->addExport(exportedType.typeName,
|
||||||
exportedType.packageName,
|
exportedType.packageName,
|
||||||
exportedType.version);
|
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;
|
static void buildContextProperties(
|
||||||
|
const Document::Ptr &doc,
|
||||||
|
TypeOfExpression &typeOf,
|
||||||
|
const QList<ContextProperty> &contextPropertyDescriptions,
|
||||||
|
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects,
|
||||||
|
QMap<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, 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()) {
|
||||||
|
Class *klass = binding->symbols().first()->asClass();
|
||||||
|
if (klass) {
|
||||||
|
FakeMetaObject::Ptr fmo = buildFakeMetaObject(klass, fakeMetaObjects, typeOf);
|
||||||
|
typeName = fmo->className();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contextProperties->insert(property.name, typeName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
@@ -513,26 +679,65 @@ FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||||
{
|
{
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> noResults;
|
m_contextProperties.clear();
|
||||||
|
m_exportedTypes.clear();
|
||||||
|
|
||||||
// this check only guards against some input errors, if document's source and AST has not
|
// 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
|
// been guarded properly the source and AST may still become empty/null while this function is running
|
||||||
if (document->source().isEmpty()
|
if (document->source().isEmpty()
|
||||||
|| !document->translationUnit()->ast())
|
|| !document->translationUnit()->ast())
|
||||||
return noResults;
|
return;
|
||||||
|
|
||||||
FindExportsVisitor finder(document);
|
FindExportsVisitor finder(document);
|
||||||
QList<ExportedQmlType> exports = finder();
|
finder();
|
||||||
if (CppModelManagerInterface *cppModelManager = CppModelManagerInterface::instance()) {
|
if (CppModelManagerInterface *cppModelManager = CppModelManagerInterface::instance()) {
|
||||||
cppModelManager->setExtraDiagnostics(
|
cppModelManager->setExtraDiagnostics(
|
||||||
document->fileName(), CppModelManagerInterface::ExportedQmlTypesDiagnostic,
|
document->fileName(), CppModelManagerInterface::ExportedQmlTypesDiagnostic,
|
||||||
finder.messages());
|
finder.messages());
|
||||||
}
|
}
|
||||||
if (exports.isEmpty())
|
|
||||||
return noResults;
|
|
||||||
|
|
||||||
return exportedQmlObjects(document, m_snapshot, exports);
|
// 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
|
||||||
|
Document::Ptr localDoc = document;
|
||||||
|
if (document->checkMode() != Document::FullCheck && !contextPropertyDescriptions.isEmpty()) {
|
||||||
|
localDoc = m_snapshot.documentFromSource(document->source(), 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)
|
||||||
|
m_exportedTypes += fmo;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const
|
||||||
|
{
|
||||||
|
return m_exportedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> FindExportedCppTypes::contextProperties() const
|
||||||
|
{
|
||||||
|
return m_contextProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
|
bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
|
||||||
@@ -540,9 +745,14 @@ bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
|
|||||||
if (!document->control())
|
if (!document->control())
|
||||||
return false;
|
return false;
|
||||||
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
||||||
|
const QByteArray setContextPropertyToken("setContextProperty");
|
||||||
if (document->control()->findIdentifier(
|
if (document->control()->findIdentifier(
|
||||||
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (document->control()->findIdentifier(
|
||||||
|
setContextPropertyToken.constData(), setContextPropertyToken.size())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
#include <languageutils/fakemetaobject.h>
|
#include <languageutils/fakemetaobject.h>
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
namespace QmlJSTools {
|
namespace QmlJSTools {
|
||||||
|
|
||||||
@@ -47,12 +48,17 @@ public:
|
|||||||
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
|
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
|
||||||
|
|
||||||
// document must have a valid source and ast for the duration of the call
|
// document must have a valid source and ast for the duration of the call
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> operator()(const CPlusPlus::Document::Ptr &document);
|
void operator()(const CPlusPlus::Document::Ptr &document);
|
||||||
|
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedTypes() const;
|
||||||
|
QMap<QString, QString> contextProperties() const;
|
||||||
|
|
||||||
static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
|
static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CPlusPlus::Snapshot m_snapshot;
|
CPlusPlus::Snapshot m_snapshot;
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> m_exportedTypes;
|
||||||
|
QMap<QString, QString> m_contextProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlJSTools
|
} // namespace QmlJSTools
|
||||||
|
@@ -717,7 +717,8 @@ void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager,
|
|||||||
CPlusPlus::CppModelManagerInterface *cppModelManager,
|
CPlusPlus::CppModelManagerInterface *cppModelManager,
|
||||||
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents)
|
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents)
|
||||||
{
|
{
|
||||||
CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
|
CppDataHash newData = qmlModelManager->cppData();
|
||||||
|
|
||||||
CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
|
CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
|
||||||
FindExportedCppTypes finder(snapshot);
|
FindExportedCppTypes finder(snapshot);
|
||||||
|
|
||||||
@@ -727,28 +728,33 @@ void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager,
|
|||||||
const bool scan = pair.second;
|
const bool scan = pair.second;
|
||||||
const QString fileName = doc->fileName();
|
const QString fileName = doc->fileName();
|
||||||
if (!scan) {
|
if (!scan) {
|
||||||
newCppTypes.remove(fileName);
|
newData.remove(fileName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder(doc);
|
finder(doc);
|
||||||
|
|
||||||
if (!exported.isEmpty())
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder.exportedTypes();
|
||||||
newCppTypes[fileName] = exported;
|
QMap<QString, QString> contextProperties = finder.contextProperties();
|
||||||
else
|
if (exported.isEmpty() && contextProperties.isEmpty()) {
|
||||||
newCppTypes.remove(fileName);
|
newData.remove(fileName);
|
||||||
|
} else {
|
||||||
|
CppData &data = newData[fileName];
|
||||||
|
data.exportedTypes = exported;
|
||||||
|
data.contextProperties = contextProperties;
|
||||||
|
}
|
||||||
|
|
||||||
doc->releaseSourceAndAST();
|
doc->releaseSourceAndAST();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
|
QMutexLocker locker(&qmlModelManager->m_cppDataMutex);
|
||||||
qmlModelManager->m_cppTypes = newCppTypes;
|
qmlModelManager->m_cppDataHash = newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelManagerInterface::CppQmlTypeHash ModelManager::cppQmlTypes() const
|
ModelManager::CppDataHash ModelManager::cppData() const
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_cppTypesMutex);
|
QMutexLocker locker(&m_cppDataMutex);
|
||||||
return m_cppTypes;
|
return m_cppDataHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryInfo ModelManager::builtins(const Document::Ptr &doc) const
|
LibraryInfo ModelManager::builtins(const Document::Ptr &doc) const
|
||||||
|
@@ -91,7 +91,7 @@ public:
|
|||||||
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
|
||||||
const QString &importUri, const QString &importVersion);
|
const QString &importUri, const QString &importVersion);
|
||||||
|
|
||||||
virtual CppQmlTypeHash cppQmlTypes() const;
|
virtual CppDataHash cppData() const;
|
||||||
|
|
||||||
virtual QmlJS::LibraryInfo builtins(const QmlJS::Document::Ptr &doc) const;
|
virtual QmlJS::LibraryInfo builtins(const QmlJS::Document::Ptr &doc) const;
|
||||||
|
|
||||||
@@ -140,8 +140,9 @@ private:
|
|||||||
|
|
||||||
QTimer *m_updateCppQmlTypesTimer;
|
QTimer *m_updateCppQmlTypesTimer;
|
||||||
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
|
QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
|
||||||
CppQmlTypeHash m_cppTypes;
|
|
||||||
mutable QMutex m_cppTypesMutex;
|
CppDataHash m_cppDataHash;
|
||||||
|
mutable QMutex m_cppDataMutex;
|
||||||
|
|
||||||
// project integration
|
// project integration
|
||||||
QMap<ProjectExplorer::Project *, ProjectInfo> m_projects;
|
QMap<ProjectExplorer::Project *, ProjectInfo> m_projects;
|
||||||
|
Reference in New Issue
Block a user