aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-08-23 17:00:25 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-08-25 20:36:33 +0200
commitede3389a3e152d7436bda6a53dd93e8847ab026e (patch)
treef04e3d7dd9d9225b9fb9ca30a1421d5e5d8ff3d5
parent3c506d0c65d355f9bd1d9b27da0315693c58c5ad (diff)
QmlCompiler: Allow setting values in sequences
Since we have write-back available now, this is not difficult anymore. This does not yet cover setting properties of value type objects stored in sequences such as "a[i].b = c". You can only set the whole element. Task-number: QTBUG-116011 Change-Id: Id5f7a19125897602880e573d5f25b025f9b91f34 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp221
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/writeback.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp2
4 files changed, 132 insertions, 98 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index ac51a50199..d4ad9a6638 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -749,7 +749,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
return;
}
- if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
+ if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
reject(u"indirect StoreElement"_s);
return;
}
@@ -761,17 +761,36 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType(
m_typeResolver->containedType(valueType)));
+ addInclude(u"qjslist.h"_s);
m_body += u"if ("_s;
if (!m_typeResolver->isIntegral(indexType))
m_body += u"QJSNumberCoercion::isArrayIndex("_s + indexName + u") && "_s;
if (!m_typeResolver->isUnsignedInteger(m_typeResolver->containedType(indexType)))
- m_body += indexName + u" >= 0 && "_s;
- m_body += indexName + u" < "_s + baseName + u".count(&"_s + baseName
- + u"))\n"_s;
- m_body += u" "_s + baseName + u".replace(&"_s + baseName
- + u", "_s + indexName + u", "_s;
+ m_body += indexName + u" >= 0"_s;
+
+ if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
+ m_body += u" && "_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
+ + u"))\n"_s;
+ m_body += u" "_s + baseName + u".replace(&"_s + baseName
+ + u", "_s + indexName + u", "_s;
+ m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
+ + u");\n"_s;
+ return;
+ }
+
+ if (m_state.isRegisterAffectedBySideEffects(base))
+ reject(u"LoadElement on a sequence potentially affected by side effects"_s);
+
+ m_body += u") {\n"_s;
+ m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
+ m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
+ + indexName + u" + 1);\n"_s;
+ m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
- + u");\n"_s;
+ + u";\n"_s;
+ m_body += u"}\n"_s;
+
+ generateWriteBack(base);
}
void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex)
@@ -952,6 +971,102 @@ void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
m_body += u"};\n";
}
+void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
+{
+ QString writeBackRegister = registerVariable(registerIndex);
+ bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(registerIndex);
+
+ for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
+ !writeBack.storedType()->isReferenceType();) {
+ if (writeBackAffectedBySideEffects)
+ reject(u"write-back of value affected by side effects"_s);
+
+ if (writeBack.isConversion())
+ reject(u"write-back of converted value"_s);
+
+ const int lookupIndex = writeBack.resultLookupIndex();
+ if (lookupIndex == -1) {
+ reject(u"write-back of non-lookup"_s);
+ break;
+ }
+
+ const QString writeBackIndexString = QString::number(lookupIndex);
+
+ const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
+ if (variant == QQmlJSRegisterContent::ScopeProperty
+ || variant == QQmlJSRegisterContent::ExtensionScopeProperty) {
+ const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
+ + writeBackIndexString
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization
+ = u"aotContext->initLoadScopeObjectPropertyLookup("_s
+ + writeBackIndexString
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ break;
+ }
+
+
+ QQmlJSRegisterContent outerContent;
+ QString outerRegister;
+ bool outerAffectedBySideEffects = false;
+ for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
+ it != end; ++it) {
+ if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
+ outerContent = it.value().content;
+ outerRegister = lookupVariable(outerContent.resultLookupIndex());
+ outerAffectedBySideEffects = it.value().affectedBySideEffects;
+ break;
+ }
+ }
+
+ if (!outerContent.isValid()) {
+ // If the lookup doesn't exist, it was killed by state merge.
+ reject(u"write-back of lookup across jumps or merges."_s);
+ break;
+ }
+
+ Q_ASSERT(!outerRegister.isEmpty());
+
+ switch (writeBack.variant()) {
+ case QQmlJSRegisterContent::ScopeProperty:
+ case QQmlJSRegisterContent::ExtensionScopeProperty:
+ Q_UNREACHABLE();
+ case QQmlJSRegisterContent::ObjectProperty:
+ case QQmlJSRegisterContent::ExtensionObjectProperty:
+ if (writeBack.scopeType()->isReferenceType()) {
+ const QString lookup = u"aotContext->writeBackObjectLookup("_s
+ + writeBackIndexString
+ + u", "_s + outerRegister
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization = u"aotContext->initGetObjectLookup("_s
+ + writeBackIndexString
+ + u", "_s + outerRegister
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ } else {
+ const QString valuePointer = contentPointer(outerContent, outerRegister);
+ const QString lookup = u"aotContext->writeBackValueLookup("_s
+ + writeBackIndexString
+ + u", "_s + valuePointer
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization = u"aotContext->initGetValueLookup("_s
+ + writeBackIndexString
+ + u", "_s + metaObject(writeBack.scopeType())
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ }
+ break;
+ default:
+ reject(u"SetLookup on value types (because of missing write-back)"_s);
+ }
+
+ writeBackRegister = outerRegister;
+ writeBack = outerContent;
+ writeBackAffectedBySideEffects = outerAffectedBySideEffects;
+ }
+}
+
void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
{
if (m_state.accumulatorOut().storedType()->accessSemantics()
@@ -1327,97 +1442,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
+ u", "_s + argType + u')';
generateLookup(lookup, initialization, preparation);
-
- QString writeBackRegister = object;
- bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(baseReg);
- for (QQmlJSRegisterContent writeBack = base; !writeBack.storedType()->isReferenceType();) {
- if (writeBackAffectedBySideEffects)
- reject(u"write-back of value affected by side effects"_s);
-
- if (writeBack.isConversion())
- reject(u"write-back of converted value"_s);
-
- const int lookupIndex = writeBack.resultLookupIndex();
- if (lookupIndex == -1) {
- reject(u"write-back of non-lookup"_s);
- break;
- }
-
- const QString writeBackIndexString = QString::number(lookupIndex);
-
- const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
- if (variant == QQmlJSRegisterContent::ScopeProperty
- || variant == QQmlJSRegisterContent::ExtensionScopeProperty) {
- const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
- + writeBackIndexString
- + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
- const QString initialization
- = u"aotContext->initLoadScopeObjectPropertyLookup("_s
- + writeBackIndexString
- + u", "_s + contentType(writeBack, writeBackRegister) + u')';
- generateLookup(lookup, initialization);
- break;
- }
-
-
- QQmlJSRegisterContent outerContent;
- QString outerRegister;
- bool outerAffectedBySideEffects = false;
- for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
- it != end; ++it) {
- if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
- outerContent = it.value().content;
- outerRegister = lookupVariable(outerContent.resultLookupIndex());
- outerAffectedBySideEffects = it.value().affectedBySideEffects;
- break;
- }
- }
-
- if (!outerContent.isValid()) {
- // If the lookup doesn't exist, it was killed by state merge.
- reject(u"write-back of lookup across jumps or merges."_s);
- break;
- }
-
- Q_ASSERT(!outerRegister.isEmpty());
-
- switch (writeBack.variant()) {
- case QQmlJSRegisterContent::ScopeProperty:
- case QQmlJSRegisterContent::ExtensionScopeProperty:
- Q_UNREACHABLE();
- case QQmlJSRegisterContent::ObjectProperty:
- case QQmlJSRegisterContent::ExtensionObjectProperty:
- if (writeBack.scopeType()->isReferenceType()) {
- const QString lookup = u"aotContext->writeBackObjectLookup("_s
- + writeBackIndexString
- + u", "_s + outerRegister
- + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
- const QString initialization = u"aotContext->initGetObjectLookup("_s
- + writeBackIndexString
- + u", "_s + outerRegister
- + u", "_s + contentType(writeBack, writeBackRegister) + u')';
- generateLookup(lookup, initialization);
- } else {
- const QString valuePointer = contentPointer(outerContent, outerRegister);
- const QString lookup = u"aotContext->writeBackValueLookup("_s
- + writeBackIndexString
- + u", "_s + valuePointer
- + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
- const QString initialization = u"aotContext->initGetValueLookup("_s
- + writeBackIndexString
- + u", "_s + metaObject(writeBack.scopeType())
- + u", "_s + contentType(writeBack, writeBackRegister) + u')';
- generateLookup(lookup, initialization);
- }
- break;
- default:
- reject(u"SetLookup on value types (because of missing write-back)"_s);
- }
-
- writeBackRegister = outerRegister;
- writeBack = outerContent;
- writeBackAffectedBySideEffects = outerAffectedBySideEffects;
- }
+ generateWriteBack(baseReg);
break;
}
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 1bbfde7358..4f195101b7 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -291,6 +291,7 @@ private:
void generateVariantEqualityComparison(const QQmlJSRegisterContent &nonStorable,
const QString &registerName, bool invert);
void generateArrayInitializer(int argc, int argv);
+ void generateWriteBack(int registerIndex);
void rejectIfNonQObjectOut(const QString &error);
void rejectIfBadArray();
diff --git a/tests/auto/qml/qmlcppcodegen/data/writeback.qml b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
index 17ddf7fd3e..8c6cb845c7 100644
--- a/tests/auto/qml/qmlcppcodegen/data/writeback.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
@@ -11,6 +11,8 @@ Person {
height: 199
}
+ property list<int> ints: [4, 3, 2, 1]
+
property outer recursive
property Person shadowable: Person {
area.width: self.area.width
@@ -28,6 +30,10 @@ Person {
shadowable.area2.y = 50
self.recursive.inner.i = 99;
+
+ self.ints[0] = 12;
+ ints[1] = 22;
+ ints[6] = 33;
}
property int inner: recursive.inner.i
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 6792947056..54020ab7c3 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -4236,6 +4236,8 @@ void tst_QmlCppCodegen::writeBack()
Person *shadowable = person->property("shadowable").value<Person *>();
QVERIFY(shadowable);
QCOMPARE(shadowable->area(), QRectF(40, 50, 16, 17));
+
+ QCOMPARE(person->property("ints"), QVariant::fromValue(QList<int>({12, 22, 2, 1, 0, 0, 33})));
}
QTEST_MAIN(tst_QmlCppCodegen)