diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2023-08-23 17:00:25 +0200 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-08-25 20:36:33 +0200 |
| commit | ede3389a3e152d7436bda6a53dd93e8847ab026e (patch) | |
| tree | f04e3d7dd9d9225b9fb9ca30a1421d5e5d8ff3d5 | |
| parent | 3c506d0c65d355f9bd1d9b27da0315693c58c5ad (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.cpp | 221 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljscodegenerator_p.h | 1 | ||||
| -rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/writeback.qml | 6 | ||||
| -rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 2 |
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 ®isterName, 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) |
