qmltc: Be aware of Component-wrapped types

While implicit components (the ones bound to Component-based property)
are covered, cases like `Component { Text{} }` are not, so fix that

Revisit the logic in the QmlIR / QQmlJSScope passes and code generator
as part of this

This might be a fairly used pattern in QML so no reason not to address
this right away, especially given that we have most of the
infrastructure in place already

While at it, bring over extra tests that some other (non-merged) commit
introduced. These give good coverage for missing cases and also exercise
the feature supported here

Task-number: QTBUG-84368
Pick-to: 6.3
Change-Id: I8f5c74fc79380566475b1139d4cc5560fac123e3
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Andrei Golubev 2021-12-14 17:20:15 +01:00
parent 844a939af5
commit 362b4bf3d6
11 changed files with 436 additions and 45 deletions

View File

@ -20,6 +20,7 @@ set(qml_sources
data/methods.qml
data/properties.qml
data/ObjectWithId.qml
data/documentWithIds.qml
data/signalHandlers.qml
data/javaScriptFunctions.qml

View File

@ -0,0 +1,96 @@
import QtQuick
Item {
property QtObject rectProperty: Rectangle {
id: rectangle
objectName: "rectangle"
}
Row {
id: row
objectName: "row"
Rectangle {
Text {
id: textInRectangle
objectName: "textInRectangle"
}
}
property list<QtObject> listOfObjects: [
Item {
id: itemInList
objectName: "itemInList"
property QtObject foobarProperty: QtObject { id: foobar; objectName: "foobar" }
},
QtObject {
id: objectInList
objectName: "objectInList"
}
]
Item {
id: item
objectName: "item"
}
}
property QtObject gridProperty: GridView {
id: gridView
objectName: "gridView"
}
TableView {
id: tableView
objectName: "tableView"
property Component before: Component {
id: beforeDelegate
Text {
id: beforeDelegateText
objectName: "beforeDelegateText"
}
}
Component {
id: beforeDelegateDefaultProperty
Text {
id: beforeDelegateDefaultPropertyText
objectName: "beforeDelegateDefaultPropertyText"
}
}
delegate: Rectangle {
id: delegateRect
objectName: "delegateRect"
}
Component {
id: afterDelegateDefaultProperty
Text {
id: afterDelegateDefaultPropertyText
objectName: "afterDelegateDefaultPropertyText"
}
}
property Component after: Component {
id: afterDelegate
Text {
id: afterDelegateText
objectName: "afterDelegateText"
}
}
}
property QtObject explicitCompProperty: Component {
id: explicitComponent
Text {
id: explicitText
objectName: "explicitText"
}
}
property QtObject sentinelProperty: QtObject {
id: sentinel
objectName: "sentinel"
}
}

View File

@ -5,6 +5,18 @@ QtObject {
property double doubleP: 0.5
property int intP: 42
property list<QtObject> listQtObjP // always list of QML objects
listQtObjP: [
Text {
id: listQtObjP_child_0
text: "child0"
},
QtObject {
property string what: "child1"
},
Item {
Rectangle { id: listQtObjP_child_2_rect }
}
]
property real realP: 2.32
property string stringP: "hello, world"
property url urlP: "https://www.qt.io/"
@ -27,10 +39,33 @@ QtObject {
required property real requiredRealP
// extra:
property Timer timerP
property list<Component> listNumP
property Timer timerP: Timer {
interval: 42
}
property list<Component> listCompP
// special:
property QtObject nullObjP: null
property var nullVarP: null
// Component-wrapped
property QtObject table: TableView {
property Component before: Component { Text { text: "beforeDelegate" } }
delegate: Text { // implicit component
text: "delegate"
}
property Component after: Component { Text { text: "afterDelegate" } }
}
property QtObject explicitCompP: Component { // explicit component
Text {
id: explicitText
text: "not a delegate"
}
}
property QtObject sentinelForComponent: QtObject {
id: sentinel
property string text: "should be correctly created"
}
}

View File

@ -36,6 +36,7 @@
#include "methods.h"
#include "properties.h"
#include "objectwithid.h"
#include "documentwithids.h"
#include "signalhandlers.h"
#include "javascriptfunctions.h"
@ -131,6 +132,7 @@ void tst_qmltc::initTestCase()
QUrl("qrc:/QmltcTests/data/methods.qml"),
QUrl("qrc:/QmltcTests/data/properties.qml"),
QUrl("qrc:/QmltcTests/data/ObjectWithId.qml"),
QUrl("qrc:/QmltcTests/data/documentWithIds.qml"),
QUrl("qrc:/QmltcTests/data/signalHandlers.qml"),
QUrl("qrc:/QmltcTests/data/javaScriptFunctions.qml"),
@ -339,7 +341,11 @@ void tst_qmltc::properties()
// extra:
QCOMPARE(propertyMetaType("timerP"), QMetaType::fromType<QQmlTimer *>());
QCOMPARE(propertyMetaType("listNumP"), QMetaType::fromType<QQmlListProperty<QQmlComponent>>());
QCOMPARE(propertyMetaType("listCompP"), QMetaType::fromType<QQmlListProperty<QQmlComponent>>());
QCOMPARE(propertyMetaType("table"), QMetaType::fromType<QObject *>());
QCOMPARE(propertyMetaType("explicitCompP"), QMetaType::fromType<QObject *>());
QCOMPARE(propertyMetaType("sentinelForComponent"), QMetaType::fromType<QObject *>());
// now, test property values:
QCOMPARE(created.boolP(), true);
@ -357,21 +363,170 @@ void tst_qmltc::properties()
QCOMPARE(created.readonlyStringP(), u"foobar"_qs);
// object bindinds:
const auto objectCtx = e.contextForObject(&created);
QQmlListReference listQtObj(&created, "listQtObjP");
QCOMPARE(listQtObj.size(), 3);
{
QQuickText *child0 = qobject_cast<QQuickText *>(listQtObj.at(0));
QVERIFY(child0);
QCOMPARE(child0->text(), u"child0"_qs);
QCOMPARE(objectCtx->objectForName("listQtObjP_child_0"), child0);
QObject *child1 = listQtObj.at(1);
QVERIFY(child1);
QCOMPARE(child1->property("what").toString(), u"child1"_qs);
QQuickItem *child2 = qobject_cast<QQuickItem *>(listQtObj.at(2));
QVERIFY(child2);
QQmlListReference data(child2, "data");
QCOMPARE(data.size(), 1);
QQuickRectangle *child2Rect = qobject_cast<QQuickRectangle *>(data.at(0));
QVERIFY(child2Rect);
QCOMPARE(objectCtx->objectForName("listQtObjP_child_2_rect"), child2Rect);
}
QQmlTimer *timer = created.timerP();
QVERIFY(timer);
QCOMPARE(timer->interval(), 42);
// nulls:
QCOMPARE(created.nullObjP(), nullptr);
QCOMPARE(created.nullVarP(), QVariant::fromValue(nullptr));
QQuickTableView *table = qobject_cast<QQuickTableView *>(created.table());
QVERIFY(table);
{
QQmlComponent *beforeDelegate = qvariant_cast<QQmlComponent *>(table->property("before"));
QVERIFY(beforeDelegate);
QQmlComponent *delegate = table->delegate();
QVERIFY(delegate);
QQmlComponent *afterDelegate = qvariant_cast<QQmlComponent *>(table->property("after"));
QVERIFY(afterDelegate);
QScopedPointer<QObject> beforeDelegateObject(beforeDelegate->create());
QVERIFY(beforeDelegateObject);
QVERIFY(qobject_cast<QQuickText *>(beforeDelegateObject.get()));
QCOMPARE(beforeDelegateObject->property("text").toString(), u"beforeDelegate"_qs);
QScopedPointer<QObject> delegateObject(delegate->create());
QVERIFY(delegateObject);
QVERIFY(qobject_cast<QQuickText *>(delegateObject.get()));
QCOMPARE(delegateObject->property("text").toString(), u"delegate"_qs);
QScopedPointer<QObject> afterDelegateObject(afterDelegate->create());
QVERIFY(afterDelegateObject);
QVERIFY(qobject_cast<QQuickText *>(afterDelegateObject.get()));
QCOMPARE(afterDelegateObject->property("text").toString(), u"afterDelegate"_qs);
}
QQmlComponent *explicitComp = qobject_cast<QQmlComponent *>(created.explicitCompP());
QVERIFY(explicitComp);
QScopedPointer<QObject> explicitCompObject(explicitComp->create());
QVERIFY(explicitCompObject);
QVERIFY(qobject_cast<QQuickText *>(explicitCompObject.get()));
QCOMPARE(explicitCompObject->property("text").toString(), u"not a delegate"_qs);
QObject *sentinelForComponent = created.sentinelForComponent();
QVERIFY(sentinelForComponent);
QCOMPARE(sentinelForComponent->property("text").toString(), u"should be correctly created"_qs);
}
void tst_qmltc::id()
void tst_qmltc::ids()
{
QQmlEngine e;
PREPEND_NAMESPACE(ObjectWithId) created(&e); // shouldn't crash here
{
QQmlEngine e;
PREPEND_NAMESPACE(ObjectWithId) created(&e); // shouldn't crash here
auto objectCtx = QQmlContextData::get(e.contextForObject(&created));
QVERIFY(objectCtx);
QCOMPARE(objectCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(objectCtx->asQQmlContext()->objectForName("objectWithId"), &created);
QCOMPARE(objectCtx->contextObject(), &created);
auto objectCtx = QQmlContextData::get(e.contextForObject(&created));
QVERIFY(objectCtx);
QCOMPARE(objectCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(objectCtx->asQQmlContext()->objectForName("objectWithId"), &created);
QCOMPARE(objectCtx->contextObject(), &created);
}
{
QQmlEngine e;
PREPEND_NAMESPACE(documentWithIds) created(&e); // shouldn't crash here
auto ctx = e.contextForObject(&created);
QVERIFY(ctx);
auto objectCtx = QQmlContextData::get(ctx);
QVERIFY(objectCtx);
QCOMPARE(objectCtx->parent(), QQmlContextData::get(e.rootContext()));
QCOMPARE(objectCtx->contextObject(), &created);
// first check that ids match object names
const auto objectNameById = [&ctx](const QString &id) {
auto object = ctx->objectForName(id);
if (!object)
return QString();
return object->objectName();
};
QCOMPARE(objectNameById("rectangle"), u"rectangle"_qs);
QCOMPARE(objectNameById("row"), u"row"_qs);
QCOMPARE(objectNameById("textInRectangle"), u"textInRectangle"_qs);
QCOMPARE(objectNameById("itemInList"), u"itemInList"_qs);
QCOMPARE(objectNameById("objectInList"), u"objectInList"_qs);
QCOMPARE(objectNameById("item"), u"item"_qs);
QCOMPARE(objectNameById("gridView"), u"gridView"_qs);
QCOMPARE(objectNameById("tableView"), u"tableView"_qs);
QCOMPARE(objectNameById("sentinel"), u"sentinel"_qs);
const auto verifyComponent = [&](QQmlComponent *component, const QString &componentId,
const QString &objectId) {
QVERIFY(component);
if (!componentId.isEmpty()) // empty string for implicit components
QCOMPARE(ctx->objectForName(componentId), component);
QCOMPARE(ctx->objectForName(objectId), nullptr);
QScopedPointer<QObject> root(component->create());
QCOMPARE(root->objectName(), objectId);
auto rootCtx = e.contextForObject(root.get());
QVERIFY(rootCtx);
QCOMPARE(rootCtx->objectForName(objectId), root.get());
};
auto explicitComponent = qobject_cast<QQmlComponent *>(created.explicitCompProperty());
verifyComponent(explicitComponent, u"explicitComponent"_qs, u"explicitText"_qs);
QQmlListReference children(&created, "data");
QCOMPARE(children.size(), 2);
QQuickTableView *table = qobject_cast<QQuickTableView *>(children.at(1));
QVERIFY(table);
QCOMPARE(ctx->objectForName(u"tableView"_qs), table);
QCOMPARE(table->objectName(), u"tableView"_qs);
auto before = qvariant_cast<QQmlComponent *>(table->property("before"));
verifyComponent(before, u"beforeDelegate"_qs, u"beforeDelegateText"_qs);
auto after = qvariant_cast<QQmlComponent *>(table->property("after"));
verifyComponent(after, u"afterDelegate"_qs, u"afterDelegateText"_qs);
auto delegate = table->delegate();
verifyComponent(delegate, /* implicit component */ QString(), u"delegateRect"_qs);
// TableView is really special when you add Component to a default
// property. see QQuickFlickablePrivate::data_append
QQmlComponent *beforeChild = nullptr;
QQmlComponent *afterChild = nullptr;
const auto tableChildren = table->children(); // QObject::children()
QVERIFY(tableChildren.size() >= 2);
for (QObject *child : tableChildren) {
auto comp = qobject_cast<QQmlComponent *>(child);
if (!comp)
continue;
// this is bad, but there doesn't seem to be any better choice
if (ctx->objectForName(u"beforeDelegateDefaultProperty"_qs) == comp)
beforeChild = comp;
else if (ctx->objectForName(u"afterDelegateDefaultProperty"_qs) == comp)
afterChild = comp;
}
// we just used ctx->objectForName() to find these components, so
// there's no point in checking the same condition in verifyComponent()
verifyComponent(beforeChild, QString(), u"beforeDelegateDefaultPropertyText"_qs);
verifyComponent(afterChild, QString(), u"afterDelegateDefaultPropertyText"_qs);
}
}
void tst_qmltc::signalHandlers()

View File

@ -50,7 +50,7 @@ private slots:
void enumerations();
void methods();
void properties();
void id();
void ids();
void signalHandlers();
void jsFunctions();

View File

@ -270,12 +270,17 @@ void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes)
requiredCppIncludes = findCppIncludes(context, objects);
};
executor.addPass(populateCppIncludes);
const auto resolveExplicitComponents = [&](const Qml2CppContext &context,
QList<Qml2CppObject> &objects) {
m_componentIndices.insert(findAndResolveExplicitComponents(context, objects));
};
const auto resolveImplicitComponents = [&](const Qml2CppContext &context,
QList<Qml2CppObject> &objects) {
m_implicitComponentMapping = findAndResolveImplicitComponents(context, objects);
m_componentIndices.insert(findAndResolveImplicitComponents(context, objects));
};
executor.addPass(resolveExplicitComponents);
executor.addPass(resolveImplicitComponents);
executor.addPass(&setObjectIds);
executor.addPass(&setObjectIds); // NB: must be after Component resolution
const auto setImmediateParents = [&](const Qml2CppContext &context,
QList<Qml2CppObject> &objects) {
m_immediateParents = findImmediateParents(context, objects);
@ -544,10 +549,11 @@ void CodeGenerator::compileObject(QQmlJSAotObject &compiled, const CodeGenObject
}
// 3. set id if it's present in the QML document
if (!m_doc->stringAt(object.irObject->idNameIndex).isEmpty()) {
Q_ASSERT(object.irObject->id >= 0);
compiled.init.body << u"// 3. set id since it exists"_qs;
compiled.init.body << u"context->setIdValue(" + QString::number(object.irObject->id)
+ u", this);";
compiled.init.body << CodeGeneratorUtility::generate_setIdValue(
u"context"_qs, object.irObject->id, u"this"_qs,
m_doc->stringAt(object.irObject->idNameIndex))
+ u";";
}
// TODO: we might want to optimize storage space when there are no object
@ -1311,17 +1317,21 @@ void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Bindin
return;
}
if (p.isList() || (binding.flags & QmlIR::Binding::IsListItem)) {
const QString refName = u"listref_" + propertyName;
const auto uniqueId = UniqueStringId(current, refName);
if (!m_listReferencesCreated.contains(uniqueId)) {
m_listReferencesCreated.insert(uniqueId);
// TODO: figure if Unicode support is needed here
current.endInit.body << u"QQmlListReference " + refName + u"(" + objectAddr
+ u", QByteArrayLiteral(\"" + propertyName + u"\"));";
current.endInit.body << u"Q_ASSERT(" + refName + u".canAppend());";
}
}
const auto setObjectBinding = [&](const QString &value) {
if (p.isList() || (binding.flags & QmlIR::Binding::IsListItem)) {
const QString refName = u"listref_" + propertyName;
const auto uniqueId = UniqueStringId(current, refName);
if (!m_listReferencesCreated.contains(uniqueId)) {
m_listReferencesCreated.insert(uniqueId);
// TODO: figure if Unicode support is needed here
current.endInit.body << u"QQmlListReference " + refName + u"(" + objectAddr
+ u", QByteArrayLiteral(\"" + propertyName + u"\"));";
current.endInit.body << u"Q_ASSERT(" + refName + u".canAppend());";
}
current.endInit.body << refName + u".append(" + value + u");";
} else {
addPropertyLine(propertyName, p, value, /* through QVariant = */ true);
@ -1333,23 +1343,34 @@ void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Bindin
// object binding separation also does not apply here
const QString objectName = makeGensym(u"sc"_qs);
Q_ASSERT(m_typeToObjectIndex.contains(bindingObject.type));
Q_ASSERT(m_implicitComponentMapping.contains(
int(m_typeToObjectIndex[bindingObject.type])));
const int index =
m_implicitComponentMapping[int(m_typeToObjectIndex[bindingObject.type])];
const int objectIndex = int(m_typeToObjectIndex[bindingObject.type]);
Q_ASSERT(m_componentIndices.contains(objectIndex));
const int index = m_componentIndices[objectIndex];
current.endInit.body << u"{"_qs;
current.endInit.body << QStringLiteral(
"auto thisContext = QQmlData::get(%1)->outerContext;")
.arg(qobjectParent);
current.endInit.body << QStringLiteral(
"auto %1 = QQmlObjectCreator::createComponent(engine, "
"QQmlEnginePrivate::get(engine)->"
"compilationUnitFromUrl(%2()), %3, %4, thisContext);")
.arg(objectName, m_urlMethod.name,
"%2, %3, %4, thisContext);")
.arg(objectName,
CodeGeneratorUtility::compilationUnitVariable.name,
QString::number(index), qobjectParent);
current.endInit.body << QStringLiteral("thisContext->installContext(QQmlData::get(%1), "
"QQmlContextData::OrdinaryObject);")
.arg(objectName);
const auto isExplicitComponent = [](const QQmlJSScope::ConstPtr &type) {
auto base = QQmlJSScope::nonCompositeBaseType(type);
return base && base->internalName() == u"QQmlComponent"_qs;
};
if (!m_doc->stringAt(bindingObject.irObject->idNameIndex).isEmpty()
&& isExplicitComponent(bindingObject.type)) {
current.endInit.body
<< CodeGeneratorUtility::generate_setIdValue(
u"thisContext"_qs, bindingObject.irObject->id, objectName,
m_doc->stringAt(bindingObject.irObject->idNameIndex))
+ u";";
}
setObjectBinding(objectName);
current.endInit.body << u"}"_qs;
break;

View File

@ -74,8 +74,8 @@ private:
QHash<QQmlJSScope::ConstPtr, qsizetype> m_typeToObjectIndex; // TODO: remove this
// parents for each type that will (also) create the type
QHash<QQmlJSScope::ConstPtr, QQmlJSScope::ConstPtr> m_immediateParents;
// mapping from to-be-wrapped object to the wrapper's object pseudo-index
QHash<int, int> m_implicitComponentMapping;
// mapping from component-wrapped object to component index (real or not)
QHash<int, int> m_componentIndices;
QQmlJSAotMethod m_urlMethod;

View File

@ -236,3 +236,12 @@ QString CodeGeneratorUtility::generate_getPrivateClass(const QString &accessor,
const QString privateType = p.privateClass();
return u"static_cast<" + privateType + u" *>(QObjectPrivate::get(" + accessor + u"))";
}
QString CodeGeneratorUtility::generate_setIdValue(const QString &context, qsizetype index,
const QString &accessor, const QString &idString)
{
Q_ASSERT(index >= 0);
const QString idComment = u"/* id: " + idString + u" */";
return context + u"->setIdValue(" + QString::number(index) + idComment + u", " + accessor
+ u")";
}

View File

@ -121,6 +121,8 @@ struct CodeGeneratorUtility
const QString &overloaded);
static QString generate_addressof(const QString &addressed);
static QString generate_getPrivateClass(const QString &accessor, const QQmlJSMetaProperty &p);
static QString generate_setIdValue(const QString &context, qsizetype index,
const QString &accessor, const QString &idString);
};
#endif // CODEGENERATORUTIL_H

View File

@ -164,6 +164,12 @@ static decltype(auto) findIrLocation(const QmlIR::Document *doc, InputIterator f
Q_LOGGING_CATEGORY(lcDefaultPasses, "qml.qmltc.compilerpasses", QtWarningMsg);
static bool isComponent(const QQmlJSScope::ConstPtr &type)
{
auto base = QQmlJSScope::nonCompositeBaseType(type);
return base && base->internalName() == u"QQmlComponent"_qs;
}
void verifyTypes(const Qml2CppContext &context, QList<Qml2CppObject> &objects)
{
const auto verifyProperty = [&](const QQmlJSMetaProperty &property,
@ -241,6 +247,13 @@ void verifyTypes(const Qml2CppContext &context, QList<Qml2CppObject> &objects)
const auto verifyBinding = [&](const QmlIR::Binding &binding,
const QQmlJSScope::ConstPtr &type) {
// QQmlComponent-wrapped types are special. consider:
// `Component { QtObject {} }`
// Component doesn't have a default property so this is an error in
// normal code
if (isComponent(type))
return;
QString propName = context.document->stringAt(binding.propertyNameIndex);
if (propName.isEmpty()) {
Q_ASSERT(type);
@ -840,6 +853,23 @@ QSet<QString> findCppIncludes(const Qml2CppContext &context, QList<Qml2CppObject
return cppIncludes;
}
QHash<int, int> findAndResolveExplicitComponents(const Qml2CppContext &context,
QList<Qml2CppObject> &objects)
{
QHash<int, int> identity;
// NB: unlike in the case of implicit components, we only need to look at
// the objects array and ignore the bindings
for (Qml2CppObject &o : objects) {
if (isComponent(o.type)) {
o.irObject->flags |= QV4::CompiledData::Object::IsComponent;
Q_ASSERT(context.typeIndices->contains(o.type));
const int index = int(context.typeIndices->value(o.type, -1));
identity[index] = index;
}
}
return identity;
}
template<typename Update>
static void updateImplicitComponents(const Qml2CppContext &context, Qml2CppObject &object,
QList<Qml2CppObject> &objects, Update update)
@ -878,11 +908,15 @@ static void updateImplicitComponents(const Qml2CppContext &context, Qml2CppObjec
QHash<int, int> findAndResolveImplicitComponents(const Qml2CppContext &context,
QList<Qml2CppObject> &objects)
{
// TODO: this pass is incomplete. Other cases include component definitions:
// `Component { Item {} }` and maybe something else
int syntheticComponentCount = 0;
QHash<int, int> indexMapping;
const auto setQQmlComponentFlag = [&](Qml2CppObject &object, int objectIndex) {
if (object.irObject->flags & QV4::CompiledData::Object::IsComponent) {
// this ir object is *already* marked as Component. which means it
// is the case of explicit component bound to Component property:
// property Component p: Component{ ... }
return;
}
object.irObject->flags |= QV4::CompiledData::Object::IsComponent;
Q_ASSERT(!indexMapping.contains(objectIndex));
// TODO: the mapping construction is very ad-hoc, it could be that the
@ -898,15 +932,36 @@ QHash<int, int> findAndResolveImplicitComponents(const Qml2CppContext &context,
return indexMapping;
}
static void setObjectId(const Qml2CppContext &context, int objectIndex,
QHash<int, int> &idToObjectIndex)
static void setObjectId(const Qml2CppContext &context, const QList<Qml2CppObject> &objects,
int objectIndex, QHash<int, int> &idToObjectIndex)
{
// TODO: this method is basically a copy-paste of
// TODO: this method is basically a (modified) version of
// QQmlComponentAndAliasResolver::collectIdsAndAliases()
QmlIR::Object *irObject = context.document->objectAt(objectIndex);
const auto isImplicitComponent = [](const Qml2CppObject &object) {
// special (to this code) way to detect implicit components after
// findAndResolveImplicitComponents() is run: unlike
// QQmlComponentAndAliasResolver we do *not* create synthetic
// components, but instead mark existing objects with IsComponent flag.
// this gives a bad side effect (for the logic here) that we cannot
// really distinguish between implicit and explicit components anymore
return object.irObject->flags & QV4::CompiledData::Object::IsComponent
&& !isComponent(object.type);
};
const Qml2CppObject &object = objects.at(objectIndex);
Q_ASSERT(object.irObject == context.document->objectAt(objectIndex));
QmlIR::Object *irObject = object.irObject;
Q_ASSERT(object.type); // assume verified
Q_ASSERT(irObject); // assume verified
if (isImplicitComponent(object)) {
// Note: somehow QmlIR ensures that implicit components have no
// idNameIndex set when setting ids for the document root. this logic
// can't do it, so reject implicit components straight away instead
return;
}
if (irObject->idNameIndex != 0) {
if (idToObjectIndex.contains(irObject->idNameIndex)) {
context.recordError(irObject->location, u"Object id is not unique"_qs);
@ -928,18 +983,28 @@ static void setObjectId(const Qml2CppContext &context, int objectIndex,
&& binding.type != QV4::CompiledData::Binding::Type_GroupProperty) {
return;
}
setObjectId(context, binding.value.objectIndex, idToObjectIndex);
setObjectId(context, objects, binding.value.objectIndex, idToObjectIndex);
});
}
void setObjectIds(const Qml2CppContext &context, QList<Qml2CppObject> &objects)
{
Q_UNUSED(objects);
QHash<int, int> idToObjectIndex;
Q_ASSERT(objects.at(0).irObject == context.document->objectAt(0));
// NB: unlike QQmlTypeCompiler, only set id for the root, completely
// ignoring the Components
setObjectId(context, 0, idToObjectIndex);
const auto set = [&](int index) {
idToObjectIndex.clear();
Q_ASSERT(objects.at(index).irObject == context.document->objectAt(index));
setObjectId(context, objects, index, idToObjectIndex);
};
// NB: in reality, we need to do the same also for implicit components, but
// for now this is good enough
for (qsizetype i = 1; i < objects.size(); ++i) {
if (isComponent(objects[i].type))
set(i);
}
set(0);
}
QHash<QQmlJSScope::ConstPtr, QQmlJSScope::ConstPtr>

View File

@ -64,6 +64,13 @@ QSet<QQmlJSMetaProperty> deferredResolveValidateAliases(const Qml2CppContext &co
// finds all required C++ include files that are needed for the generated C++
QSet<QString> findCppIncludes(const Qml2CppContext &context, QList<Qml2CppObject> &objects);
// finds and resolves explicit QQmlComponent types. returns (essentially) a set
// of QmlIR::Object indices that represent types derived from QQmlComponent. the
// return value is a QHash<> to be compatible with
// findAndResolveImplicitComponents()
QHash<int, int> findAndResolveExplicitComponents(const Qml2CppContext &context,
QList<Qml2CppObject> &objects);
// finds and resolves implicit QQmlComponent types. returns a mapping from
// QmlIR::Object that is being wrapped into a QQmlComponent to an index of that
// implicit wrapper, which is a synthetic QmlIR::Object