From fe11a7dcc71adf017553d430a96a77020ca4c4d8 Mon Sep 17 00:00:00 2001 From: Andrei Golubev Date: Tue, 26 Jul 2022 14:32:23 +0200 Subject: qmltc: Make explicit component ids accessible during object creation Explicit components could be accessed by id like regular objects. Make this work in qmltc by revisiting the object creation procedure - store explicit components along with pure QML types in QQmltcObjectCreationBase: xxxxxxxx x'x'x'x'x'|yyyyy... ^ ^ ^ pure explicit types from base class types components As a drive by, fix unset required property in one test file and add a bunch of other test files to the initTestCase() check Change-Id: Ibdaa430d692c81101853f51dae1da462a57a9980 Reviewed-by: Fabian Kosmale (cherry picked from commit 12ce72825995529e203c98adbe636a01b995ff13) --- tests/auto/qml/qmltc/QmltcTests/componentTypes.qml | 8 +++ tests/auto/qml/qmltc/QmltcTests/properties.qml | 1 + tests/auto/qml/qmltc/tst_qmltc.cpp | 10 ++++ tools/qmltc/qmltccompiler.cpp | 68 +++++++++++++--------- tools/qmltc/qmltccompilerpieces.h | 3 +- tools/qmltc/qmltcvisitor.cpp | 50 +++++++++++----- tools/qmltc/qmltcvisitor.h | 7 ++- 7 files changed, 101 insertions(+), 46 deletions(-) diff --git a/tests/auto/qml/qmltc/QmltcTests/componentTypes.qml b/tests/auto/qml/qmltc/QmltcTests/componentTypes.qml index 48b68e9112..d45f2399b4 100644 --- a/tests/auto/qml/qmltc/QmltcTests/componentTypes.qml +++ b/tests/auto/qml/qmltc/QmltcTests/componentTypes.qml @@ -1,5 +1,7 @@ import QtQuick Item { + id: root + ComponentType { // normal type here id: normal property string text: "indirect component" @@ -22,4 +24,10 @@ Item { TableView { delegate: ComponentType { id: accessibleDelegate } } + + property alias accessibleNormalProgress: accessibleNormal.progress + property alias accessibleNormalUrl: accessibleNormal.url + property url urlClone: root.accessibleNormalUrl + + property alias delegateUrlClone: accessibleDelegate.url } diff --git a/tests/auto/qml/qmltc/QmltcTests/properties.qml b/tests/auto/qml/qmltc/QmltcTests/properties.qml index 896cd88eac..ca827519d5 100644 --- a/tests/auto/qml/qmltc/QmltcTests/properties.qml +++ b/tests/auto/qml/qmltc/QmltcTests/properties.qml @@ -37,6 +37,7 @@ QtObject { default property QtObject defaultObjP readonly property string readonlyStringP: "foobar" required property real requiredRealP + requiredRealP: 3.2 // extra: property Timer timerP: Timer { diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index e15870dcaa..5a0d7eea9d 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -121,6 +121,11 @@ void tst_qmltc::initTestCase() QUrl("qrc:/QmltcTests/ObjectWithId.qml"), QUrl("qrc:/QmltcTests/documentWithIds.qml"), QUrl("qrc:/QmltcTests/importNamespace.qml"), + QUrl("qrc:/QmltcTests/ComponentType.qml"), + QUrl("qrc:/QmltcTests/componentTypes.qml"), + QUrl("qrc:/QmltcTests/gradients.qml"), + QUrl("qrc:/QmltcTests/qjsvalueAssignments.qml"), + QUrl("qrc:/QmltcTests/deferredProperties.qml"), QUrl("qrc:/QmltcTests/deferredProperties_group.qml"), QUrl("qrc:/QmltcTests/deferredProperties_attached.qml"), @@ -582,6 +587,11 @@ void tst_qmltc::componentTypes() QQuickTableView *table = qobject_cast(children.at(3)); QVERIFY(table); QCOMPARE(ctx->objectForName(u"accessibleDelegate"_s), table->delegate()); + + QCOMPARE(created.accessibleNormalProgress(), + children.at(1)->property("progress").toDouble()); + QCOMPARE(created.urlClone(), QUrl("qrc:/QmltcTests/componentTypes.qml")); + QCOMPARE(created.delegateUrlClone(), QUrl("qrc:/QmltcTests/ComponentType.qml")); } } diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index d91ceaadf6..66d10eaa83 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -571,9 +571,11 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a // doing the above allows us to lookup id object by index (fast) queryIdFrame.outVar = u"alias_objectById_" + aliasExprBits.front(); // unique enough - queryIdFrame.prologue << u"auto " + queryIdFrame.outVar + u" = static_cast<" - + type->internalName() + u"*>(context->idValue(" + QString::number(id) - + u"));"; + const QString cppType = (m_visitor->qmlComponentIndex(type) == -1) + ? type->internalName() + : u"QQmlComponent"_s; + queryIdFrame.prologue << u"auto " + queryIdFrame.outVar + u" = static_cast<" + cppType + + u"*>(context->idValue(" + QString::number(id) + u"));"; queryIdFrame.prologue << u"Q_ASSERT(" + queryIdFrame.outVar + u");"; frames.push(queryIdFrame); @@ -742,10 +744,13 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a // type, not on the private one -- otherwise, you can't connect to a // private property signal in C++ and so it is useless (hence, use // public type) + const QString cppType = (m_visitor->qmlComponentIndex(result.owner) == -1) + ? result.owner->internalName() + : u"QQmlComponent"_s; const QString latestAccessorNonPrivate = notifyFrames.top().outVar; - current.endInit.body << u"QObject::connect(" + latestAccessorNonPrivate + u", &" - + result.owner->internalName() + u"::" + notifyName + u", this, &" - + current.cppType + u"::" + compilationData.notify + u");"; + current.endInit.body << u"QObject::connect(" + latestAccessorNonPrivate + u", &" + cppType + + u"::" + notifyName + u", this, &" + current.cppType + u"::" + + compilationData.notify + u");"; current.endInit.body += notifyEpilogue; current.endInit.body << u"}"_s; } @@ -812,7 +817,7 @@ void QmltcCompiler::compileBinding(QmltcType ¤t, const QQmlJSMetaPropertyB const auto &listName = m_uniques[UniqueStringId(current, propertyName)].qmlListVariableName; Q_ASSERT(!listName.isEmpty()); - current.endInit.body << u"%1.append(%2);"_qs.arg(listName, value); + current.endInit.body << u"%1.append(%2);"_s.arg(listName, value); } else { assignToProperty(p, value, /* constructFromQObject = */ true); } @@ -886,38 +891,47 @@ void QmltcCompiler::compileBinding(QmltcType ¤t, const QQmlJSMetaPropertyB // special case of implicit or explicit component: if (qsizetype index = m_visitor->qmlComponentIndex(object); index >= 0) { - // TODO: or do this in current.init? - yes, because otherwise - // components are not going to be id-accessible, which is bad - const QString objectName = newSymbol(u"sc"_s); - current.endInit.body << u"{"_s; - current.endInit.body << QStringLiteral( - "auto thisContext = QQmlData::get(%1)->outerContext;") - .arg(qobjectParent); - current.endInit.body << QStringLiteral( - "auto %1 = QQmlObjectCreator::createComponent(engine, " - "%2, %3, %4, thisContext);") - .arg(objectName, - generate_callCompilationUnit(m_urlMethodName), - QString::number(index), qobjectParent); - current.endInit.body << QStringLiteral("thisContext->installContext(QQmlData::get(%1), " - "QQmlContextData::OrdinaryObject);") - .arg(objectName); + + const qsizetype creationIndex = m_visitor->creationIndex(object); + + QStringList *block = (creationIndex == -1) ? ¤t.endInit.body : ¤t.init.body; + *block << u"{"_s; + *block << QStringLiteral("auto thisContext = QQmlData::get(%1)->outerContext;") + .arg(qobjectParent); + *block << QStringLiteral("auto %1 = QQmlObjectCreator::createComponent(engine, " + "%2, %3, %4, thisContext);") + .arg(objectName, generate_callCompilationUnit(m_urlMethodName), + QString::number(index), qobjectParent); + *block << QStringLiteral("thisContext->installContext(QQmlData::get(%1), " + "QQmlContextData::OrdinaryObject);") + .arg(objectName); // objects wrapped in implicit components do not have visible ids, // however, explicit components can have an id and that one is going // to be visible in the common document context - if (!object->isComponentRootElement()) { + if (creationIndex != -1) { + // explicit component + Q_ASSERT(object->isComposite()); + Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s); + if (int id = m_visitor->runtimeId(object); id >= 0) { QString idString = m_visitor->addressableScopes().id(object); if (idString.isEmpty()) idString = u""_s; - QmltcCodeGenerator::generate_setIdValue(¤t.endInit.body, u"thisContext"_s, - id, objectName, idString); + QmltcCodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName, + idString); } + + const QString creationIndexStr = QString::number(creationIndex); + *block << QStringLiteral("creator->set(%1, %2);").arg(creationIndexStr, objectName); + Q_ASSERT(block == ¤t.init.body); + current.endInit.body + << QStringLiteral("auto %1 = creator->get<%2>(%3);") + .arg(objectName, u"QQmlComponent"_s, creationIndexStr); } addObjectBinding(objectName); - current.endInit.body << u"}"_s; + *block << u"}"_s; break; } diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h index 140a30745d..37eb82d577 100644 --- a/tools/qmltc/qmltccompilerpieces.h +++ b/tools/qmltc/qmltccompilerpieces.h @@ -618,7 +618,8 @@ inline QString QmltcCodeGenerator::generate_typeCount(Predicate p) const // add this document's type counts minus document root Q_ASSERT(visitor->pureQmlTypes().size() > 0); - components << QString::number(visitor->pureQmlTypes().size() - 1); + Q_ASSERT(visitor->typeCount() >= visitor->pureQmlTypes().size()); + components << QString::number(visitor->typeCount() - 1); // traverse types with QML base classes for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) { diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 7f6ad4715c..efe573d05d 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -28,19 +28,28 @@ static QString uniqueNameFromPieces(const QStringList &pieces, QHashisComposite()) + return false; + auto base = type->baseType(); + return base && base->internalName() == u"QQmlComponent"; +} + +static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type) +{ + if (!type->isComposite()) + return false; + const auto cppBase = QQmlJSScope::nonCompositeBaseType(type); + const bool isComponentBased = (cppBase && cppBase->internalName() == u"QQmlComponent"); + return type->isComponentRootElement() && !isComponentBased; +} + static bool isOrUnderComponent(QQmlJSScope::ConstPtr type) { Q_ASSERT(type->isComposite()); // we're dealing with composite types here for (; type; type = type->parentScope()) { - if (type->isWrappedInImplicitComponent()) - return true; - // for a composite type, its internalName() is guaranteed to not be a - // QQmlComponent. we need to detect a case with `Component {}` QML type - // where the *immediate* base type of the current type will be the - // QQmlComponent. note that non-composite base is different: if our type - // is not a direct child of QQmlComponent, then it is a good type that - // we have to compile - if (auto base = type->baseType(); base && base->internalName() == u"QQmlComponent") + if (isExplicitComponent(type) || isImplicitComponent(type)) return true; } return false; @@ -447,6 +456,7 @@ void QmltcVisitor::postVisitResolve( // find all "pure" QML types m_pureQmlTypes.reserve(m_qmlTypes.size()); + QList explicitComponents; for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) { const QQmlJSScope::ConstPtr &type = m_qmlTypes.at(i); @@ -454,14 +464,26 @@ void QmltcVisitor::postVisitResolve( // root is special: we compile Component roots. root is also never // deferred, so in case `isOrUnderDeferred(type)` returns true, we // always continue here - if (type != m_exportedRootScope) + if (type != m_exportedRootScope) { + // if a type is an explicit component, its id "leaks" into the + // document context + if (isExplicitComponent(type)) + explicitComponents.append(type); continue; + } } - m_pureTypeIndices[type] = m_pureQmlTypes.size(); + m_creationIndices[type] = m_pureQmlTypes.size(); m_pureQmlTypes.append(type); } + // add explicit components to the object creation indices + { + qsizetype index = 0; + for (const QQmlJSScope::ConstPtr &c : qAsConst(explicitComponents)) + m_creationIndices[c] = m_pureQmlTypes.size() + index++; + } + // filter out deferred types { QList filteredQmlTypesWithQmlBases; @@ -498,14 +520,12 @@ void QmltcVisitor::postVisitResolve( int syntheticCreationIndex = 0; const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) { // explicit component - if (auto base = type->baseType(); base && base->internalName() == u"QQmlComponent"_s) { + if (isExplicitComponent(type)) { m_syntheticTypeIndices[type] = m_qmlIrObjectIndices.value(type, -1); return true; } // implicit component - const auto cppBase = QQmlJSScope::nonCompositeBaseType(type); - const bool isComponentBased = (cppBase && cppBase->internalName() == u"QQmlComponent"_s); - if (type->isComponentRootElement() && !isComponentBased) { + if (isImplicitComponent(type)) { const int index = int(qmlScopeCount) + syntheticCreationIndex++; m_syntheticTypeIndices[type] = index; return true; diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h index 0ac0562a6b..04f18b96d5 100644 --- a/tools/qmltc/qmltcvisitor.h +++ b/tools/qmltc/qmltcvisitor.h @@ -49,11 +49,12 @@ public: qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const { - Q_ASSERT(m_pureTypeIndices.contains(type)); Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); - return m_pureTypeIndices[type]; + return m_creationIndices.value(type, -1); } + qsizetype typeCount() const { return m_creationIndices.size(); } + qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const { Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); @@ -110,7 +111,7 @@ protected: QSet m_cppIncludes; // all C++ includes found from QQmlJSScope hierarchy QList m_pureQmlTypes; // the ones not under QQmlComponent - QHash m_pureTypeIndices; + QHash m_creationIndices; QHash m_syntheticTypeIndices; QHash m_qmlIrObjectIndices; -- cgit v1.2.3