forked from qt-creator/qt-creator
qmljs: find and add singleton and url based registrations in cpp files
parse all qmlRegisterSingletonType variants, and the QUrl based qmlRegisterType from .cpp files. Add types for them. Currently non creatable types are still offered as completions, and Url and QJSValue registrations do not add any property. Task-number: QTCREATORBUG-12894 Change-Id: I24f1c6733575a3ff6b93c5fa895180ae73c9e4fb Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com> Reviewed-by: Tim Jenssen <tim.jenssen@digia.com> Reviewed-by: Fawzi Mohamed <fawzi.mohamed@digia.com>
This commit is contained in:
@@ -50,6 +50,9 @@ public:
|
||||
LanguageUtils::ComponentVersion version;
|
||||
Scope *scope;
|
||||
QString typeExpression;
|
||||
QString url;
|
||||
bool isCreatable;
|
||||
bool isSingleton;
|
||||
};
|
||||
|
||||
class ContextProperty {
|
||||
@@ -71,6 +74,24 @@ class FindExportsVisitor : protected ASTVisitor
|
||||
QList<CPlusPlus::Document::DiagnosticMessage> _messages;
|
||||
|
||||
public:
|
||||
enum RegistrationFunction {
|
||||
InvalidRegistrationFunction,
|
||||
// template<typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
|
||||
// or template<typename T, int metaObjectRevision> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
|
||||
QmlRegisterType4,
|
||||
// int qmlRegisterType(const QUrl & url, const char * uri, int versionMajor, int versionMinor, const char * qmlName)
|
||||
QmlRegisterType5,
|
||||
// template<typename T> int qmlRegisterSingletonType(const char * uri, int versionMajor, int versionMinor, const char * typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
|
||||
QmlRegisterSingletonTypeCallback1,
|
||||
// int qmlRegisterSingletonType(const char * uri, int versionMajor, int versionMinor, const char * typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback)
|
||||
QmlRegisterSingletonTypeCallback2,
|
||||
// int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
|
||||
QmlRegisterSingletonTypeUrl,
|
||||
// template<typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
|
||||
// or template<typename T, int metaObjectRevision> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
|
||||
QmlRegisterUncreatableType
|
||||
};
|
||||
|
||||
FindExportsVisitor(CPlusPlus::Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit())
|
||||
, _doc(doc)
|
||||
@@ -122,63 +143,112 @@ protected:
|
||||
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)
|
||||
RegistrationFunction registrationFunction = InvalidRegistrationFunction;
|
||||
TypeIdAST *typeId = 0;
|
||||
if (TemplateIdAST *templateId = idExp->name->asTemplateId()) {
|
||||
if (!templateId->identifier_token)
|
||||
return false;
|
||||
// should just check for a generic ExpressionAST?
|
||||
NumericLiteralAST *metaRevision =
|
||||
templateId->template_argument_list->next->value->asNumericLiteral();
|
||||
if (!metaRevision)
|
||||
|
||||
// check the name
|
||||
const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
|
||||
if (!templateIdentifier)
|
||||
return false;
|
||||
const QString callName = QString::fromUtf8(templateIdentifier->chars());
|
||||
if (callName == QLatin1String("qmlRegisterType"))
|
||||
registrationFunction = QmlRegisterType4;
|
||||
else if (callName == QLatin1String("qmlRegisterSingletonType"))
|
||||
registrationFunction = QmlRegisterSingletonTypeCallback1;
|
||||
else if (callName == QLatin1String("qmlRegisterUncreatableType"))
|
||||
registrationFunction = QmlRegisterUncreatableType;
|
||||
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;
|
||||
}
|
||||
|
||||
typeId = templateId->template_argument_list->value->asTypeId();
|
||||
if (!typeId)
|
||||
return false;
|
||||
} else if (idExp->name) {
|
||||
QByteArray fName;
|
||||
if (SimpleNameAST *simpleName = idExp->name->asSimpleName()) {
|
||||
if (const Identifier *id = translationUnit()->identifier(simpleName->identifier_token))
|
||||
fName = QByteArray(id->chars(), id->size());
|
||||
}
|
||||
if (fName == "qmlRegisterType")
|
||||
registrationFunction = QmlRegisterType5;
|
||||
else if (fName == "qmlRegisterSingletonType")
|
||||
registrationFunction = QmlRegisterSingletonTypeCallback2;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
|
||||
if (!typeId)
|
||||
return false;
|
||||
|
||||
// must have four arguments for qmlRegisterType and five for qmlRegisterUncreatableType
|
||||
// must have at least four arguments
|
||||
if (!ast->expression_list
|
||||
|| !ast->expression_list->value || !ast->expression_list->next
|
||||
|| !ast->expression_list->next->value || !ast->expression_list->next->next
|
||||
|| !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
|
||||
|| !ast->expression_list->next->next->next->value)
|
||||
return false;
|
||||
if (argCount == 4 && ast->expression_list->next->next->next->next)
|
||||
switch (registrationFunction) {
|
||||
case InvalidRegistrationFunction:
|
||||
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
|
||||
case QmlRegisterType4:
|
||||
break;
|
||||
case QmlRegisterType5:
|
||||
case QmlRegisterSingletonTypeCallback1:
|
||||
case QmlRegisterSingletonTypeCallback2:
|
||||
case QmlRegisterSingletonTypeUrl:
|
||||
case QmlRegisterUncreatableType:
|
||||
if (!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;
|
||||
}
|
||||
ExpressionAST *uriExp = 0;
|
||||
ExpressionAST *majorVersionExp = 0;
|
||||
ExpressionAST *minorVersionExp = 0;
|
||||
ExpressionAST *nameExp = 0;
|
||||
if (registrationFunction == QmlRegisterType5) {
|
||||
uriExp = ast->expression_list->next->value;
|
||||
majorVersionExp = ast->expression_list->next->next->value;
|
||||
minorVersionExp = ast->expression_list->next->next->next->value;
|
||||
nameExp = ast->expression_list->next->next->next->next->value;
|
||||
} else if (registrationFunction == QmlRegisterSingletonTypeCallback2
|
||||
&& (!ast->expression_list->next->value->asNumericLiteral()
|
||||
|| ast->expression_list->next->next->next->value->asNumericLiteral())) {
|
||||
// discriminate between QmlRegisterSingletonTypeCallback2 and QmlRegisterSingletonTypeUrl,
|
||||
// this is very rough check, improve?
|
||||
registrationFunction = QmlRegisterSingletonTypeUrl;
|
||||
uriExp = ast->expression_list->next->value;
|
||||
majorVersionExp = ast->expression_list->next->next->value;
|
||||
minorVersionExp = ast->expression_list->next->next->next->value;
|
||||
nameExp = ast->expression_list->next->next->next->next->value;
|
||||
} else {
|
||||
uriExp = ast->expression_list->value;
|
||||
majorVersionExp = ast->expression_list->next->value;
|
||||
minorVersionExp = ast->expression_list->next->next->value;
|
||||
nameExp = ast->expression_list->next->next->next->value;
|
||||
}
|
||||
const StringLiteral *nameLit = 0;
|
||||
if (StringLiteralAST *nameAst = skipStringCall(ast->expression_list->next->next->next->value)->asStringLiteral())
|
||||
if (StringLiteralAST *nameAst = skipStringCall(nameExp)->asStringLiteral())
|
||||
nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
|
||||
if (!nameLit) {
|
||||
unsigned line, column;
|
||||
translationUnit()->getTokenStartPosition(ast->expression_list->next->next->next->value->firstToken(), &line, &column);
|
||||
translationUnit()->getTokenStartPosition(nameExp->firstToken(), &line, &column);
|
||||
_messages += Document::DiagnosticMessage(
|
||||
Document::DiagnosticMessage::Warning,
|
||||
_doc->fileName(),
|
||||
@@ -188,9 +258,9 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the first argument is a string literal, things are easy
|
||||
// if the uri is a string literal, things are easy
|
||||
QString packageName;
|
||||
if (StringLiteralAST *packageAst = skipStringCall(ast->expression_list->value)->asStringLiteral()) {
|
||||
if (StringLiteralAST *packageAst = skipStringCall(uriExp)->asStringLiteral()) {
|
||||
const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
|
||||
packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
|
||||
}
|
||||
@@ -198,7 +268,7 @@ protected:
|
||||
// Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
|
||||
// in the enclosing compound statement
|
||||
QString uriNameString;
|
||||
if (IdExpressionAST *uriName = ast->expression_list->value->asIdExpression())
|
||||
if (IdExpressionAST *uriName = uriExp->asIdExpression())
|
||||
uriNameString = stringOf(uriName);
|
||||
if (packageName.isEmpty() && !uriNameString.isEmpty() && _compound) {
|
||||
for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
|
||||
@@ -249,16 +319,21 @@ protected:
|
||||
"Qt Creator know about a likely URI."));
|
||||
}
|
||||
|
||||
// second and third argument must be integer literals
|
||||
// version arguments must be integer literals
|
||||
const NumericLiteral *majorLit = 0;
|
||||
const NumericLiteral *minorLit = 0;
|
||||
if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
|
||||
if (NumericLiteralAST *majorAst = majorVersionExp->asNumericLiteral())
|
||||
majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
|
||||
if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
|
||||
if (NumericLiteralAST *minorAst = minorVersionExp->asNumericLiteral())
|
||||
minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
|
||||
|
||||
// build the descriptor
|
||||
ExportedQmlType exportedType;
|
||||
exportedType.isSingleton = registrationFunction == QmlRegisterSingletonTypeCallback1
|
||||
|| registrationFunction == QmlRegisterSingletonTypeCallback2
|
||||
|| registrationFunction == QmlRegisterSingletonTypeUrl;
|
||||
exportedType.isCreatable = !exportedType.isSingleton
|
||||
&& registrationFunction != QmlRegisterUncreatableType;
|
||||
exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
|
||||
exportedType.packageName = packageName;
|
||||
if (majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
|
||||
@@ -272,12 +347,27 @@ protected:
|
||||
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.bytesBegin(), last.bytesEnd() - begin.bytesBegin()));
|
||||
|
||||
if (typeId){
|
||||
// add 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.bytesBegin(), last.bytesEnd() - begin.bytesBegin()));
|
||||
} else if (registrationFunction == QmlRegisterSingletonTypeCallback2) {
|
||||
// we cannot really do much with generated QJSValue values
|
||||
} else if (registrationFunction == QmlRegisterSingletonTypeUrl
|
||||
|| registrationFunction == QmlRegisterType5) {
|
||||
// try to recover QUrl("literal")
|
||||
if (ast->expression_list && ast->expression_list->value) {
|
||||
ExpressionAST *urlAst = skipStringCall(skipQUrl(ast->expression_list->value, translationUnit()));
|
||||
if (StringLiteralAST *uriAst = urlAst->asStringLiteral()) {
|
||||
const StringLiteral *urlLit = translationUnit()->stringLiteral(uriAst->literal_token);
|
||||
exportedType.url = QString::fromUtf8(urlLit->chars(), urlLit->size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCWarning(QmlJS::qmljsLog()) << "missing template type for registrationFunction " << registrationFunction;
|
||||
}
|
||||
_exportedTypes += exportedType;
|
||||
|
||||
return true;
|
||||
@@ -342,6 +432,32 @@ protected:
|
||||
return ast;
|
||||
}
|
||||
|
||||
static ExpressionAST *skipQUrl(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;
|
||||
|
||||
// QUrl(foo) -> foo
|
||||
if (SimpleNameAST *simpleName = idExp->name->asSimpleName()) {
|
||||
const Identifier *id = translationUnit->identifier(simpleName->identifier_token);
|
||||
if (!id)
|
||||
return ast;
|
||||
if (QByteArray(id->chars(), id->size()) == "QUrl")
|
||||
return call->expression_list->value;
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
bool checkForSetContextProperty(CallAST *ast)
|
||||
{
|
||||
// check whether ast->base_expression has the 'setContextProperty' name
|
||||
@@ -631,7 +747,8 @@ static LanguageUtils::FakeMetaObject::Ptr buildFakeMetaObject(
|
||||
static void buildExportedQmlObjects(
|
||||
TypeOfExpression &typeOf,
|
||||
const QList<ExportedQmlType> &cppExports,
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects)
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *fakeMetaObjects,
|
||||
QList<LanguageUtils::FakeMetaObject::Ptr> *extraFakeMetaObjects)
|
||||
{
|
||||
using namespace LanguageUtils;
|
||||
|
||||
@@ -639,12 +756,21 @@ static void buildExportedQmlObjects(
|
||||
return;
|
||||
|
||||
foreach (const ExportedQmlType &exportedType, cppExports) {
|
||||
Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
|
||||
Class *klass = 0;
|
||||
if (!exportedType.typeExpression.isEmpty())
|
||||
klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
|
||||
// TODO: something smarter with exportedType.url
|
||||
// accepts a null klass
|
||||
FakeMetaObject::Ptr fmo = buildFakeMetaObject(klass, fakeMetaObjects, typeOf);
|
||||
fmo->addExport(exportedType.typeName,
|
||||
exportedType.packageName,
|
||||
exportedType.version);
|
||||
fmo->setIsCreatable(exportedType.isCreatable);
|
||||
fmo->setIsSingleton(exportedType.isSingleton);
|
||||
if (!klass) {
|
||||
fmo->setClassName(QLatin1String("__autogen__") + exportedType.typeName);
|
||||
extraFakeMetaObjects->append(fmo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,9 +855,10 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||
TypeOfExpression typeOf;
|
||||
typeOf.init(localDoc, m_snapshot);
|
||||
QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> fakeMetaObjects;
|
||||
QList<LanguageUtils::FakeMetaObject::Ptr> extraFakeMetaObjects;
|
||||
|
||||
// generate the exports from qmlRegisterType
|
||||
buildExportedQmlObjects(typeOf, exports, &fakeMetaObjects);
|
||||
buildExportedQmlObjects(typeOf, exports, &fakeMetaObjects, &extraFakeMetaObjects);
|
||||
|
||||
// add the types from the context properties and create a name->cppname map
|
||||
// also expose types where necessary
|
||||
@@ -739,11 +866,15 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||
&fakeMetaObjects, &m_contextProperties);
|
||||
|
||||
// convert to list of FakeMetaObject::ConstPtr
|
||||
m_exportedTypes.reserve(fakeMetaObjects.size());
|
||||
m_exportedTypes.reserve(fakeMetaObjects.size() + extraFakeMetaObjects.size());
|
||||
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) {
|
||||
fmo->updateFingerprint();
|
||||
m_exportedTypes += fmo;
|
||||
}
|
||||
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, extraFakeMetaObjects) {
|
||||
fmo->updateFingerprint();
|
||||
m_exportedTypes += fmo;
|
||||
}
|
||||
}
|
||||
|
||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const
|
||||
@@ -760,13 +891,18 @@ bool FindExportedCppTypes::maybeExportsTypes(const CPlusPlus::Document::Ptr &doc
|
||||
{
|
||||
if (!document->control())
|
||||
return false;
|
||||
const QByteArray qmlRegisterTypeToken("qmlRegisterType");
|
||||
const QByteArray qmlRegisterSingletonTypeToken("qmlRegisterType");
|
||||
const QByteArray qmlRegisterTypeToken("qmlRegisterSingletonType");
|
||||
const QByteArray qmlRegisterUncreatableTypeToken("qmlRegisterUncreatableType");
|
||||
const QByteArray setContextPropertyToken("setContextProperty");
|
||||
if (document->control()->findIdentifier(
|
||||
qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
|
||||
return true;
|
||||
}
|
||||
if (document->control()->findIdentifier(
|
||||
qmlRegisterSingletonTypeToken.constData(), qmlRegisterSingletonTypeToken.size())) {
|
||||
return true;
|
||||
}
|
||||
if (document->control()->findIdentifier(
|
||||
qmlRegisterUncreatableTypeToken.constData(), qmlRegisterUncreatableTypeToken.size())) {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user