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:
parent
844a939af5
commit
362b4bf3d6
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -50,7 +50,7 @@ private slots:
|
|||
void enumerations();
|
||||
void methods();
|
||||
void properties();
|
||||
void id();
|
||||
void ids();
|
||||
|
||||
void signalHandlers();
|
||||
void jsFunctions();
|
||||
|
|
|
@ -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 ¤t, 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 ¤t, 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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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")";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue