aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-02-24 08:57:44 +0100
committerUlf Hermann <ulf.hermann@qt.io>2022-03-14 20:53:52 +0100
commit6eff3465b5192e40343f0486ef7076da2ed28bed (patch)
tree5b1b4f65b2396ebd7152ab8f13300e5f5dd33421
parent9de2b11a7033a1969156277bafa848b5c178baa1 (diff)
QmlCompiler: Implement generate_DefineArray
Using the type cloning and adaption mechanism we can now determine what kind of list we have to create in order to avoid a later conversion. We can even propagate the type adjustment into the element types we read. Fixes: QTBUG-100157 Change-Id: Ia2f160ebae56f39ee5946f49d2f8c5b4986a6b77 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp41
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h3
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp38
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp2
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp9
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp23
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h3
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/typedArray.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp29
9 files changed, 136 insertions, 18 deletions
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
index cce7e141eb..6c3a0d71ab 100644
--- a/src/qmlcompiler/qqmljsbasicblocks.cpp
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -140,6 +140,14 @@ void QQmlJSBasicBlocks::generate_ThrowException()
m_skipUntilNextLabel = true;
}
+void QQmlJSBasicBlocks::generate_DefineArray(int argc, int)
+{
+ if (argc == 0)
+ return; // empty array/list, nothing to do
+
+ m_arrayDefinitions.append(currentInstructionOffset());
+}
+
void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
{
if (offset < 0)
@@ -377,6 +385,39 @@ void QQmlJSBasicBlocks::adjustTypes()
using NewVirtualRegisters = NewFlatMap<int, QQmlJSRegisterContent>;
QHash<int, QList<int>> liveConversions;
+
+ // Handle the array definitions first.
+ // Changing the array type changes the expected element types.
+ for (int instructionOffset : m_arrayDefinitions) {
+ auto it = m_readerLocations.find(instructionOffset);
+ if (it == m_readerLocations.end())
+ continue;
+
+ const InstructionAnnotation &annotation = m_annotations[instructionOffset];
+
+ Q_ASSERT(it->trackedTypes.length() == 1);
+ Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
+ Q_ASSERT(!annotation.readRegisters.isEmpty());
+
+ m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values());
+
+ // Now we don't adjust the type we store, but rather the type we expect to read. We
+ // can do this because we've tracked the read type when we defined the array in
+ // QQmlJSTypePropagator.
+ if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
+ m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(annotation.readRegisters.begin().value()),
+ valueType);
+ }
+
+ for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) {
+ for (int conversion : conversions)
+ liveConversions[conversion].append(it->trackedRegister);
+ }
+
+ m_readerLocations.erase(it);
+ }
+
for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) {
for (int conversion : conversions)
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
index e895dc7fe6..e5a39104db 100644
--- a/src/qmlcompiler/qqmljsbasicblocks_p.h
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -88,6 +88,8 @@ private:
void generate_Ret() override;
void generate_ThrowException() override;
+ void generate_DefineArray(int argc, int argv) override;
+
enum JumpMode { Unconditional, Conditional };
void processJump(int offset, JumpMode mode);
void populateBasicBlocks();
@@ -97,6 +99,7 @@ private:
InstructionAnnotations m_annotations;
QFlatMap<int, BasicBlock> m_basicBlocks;
QHash<int, RegisterAccess> m_readerLocations;
+ QList<int> m_arrayDefinitions;
bool m_skipUntilNextLabel = false;
bool m_hadBackJumps = false;
};
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 7867a244c2..dd564207d8 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -1561,15 +1561,37 @@ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(args);
- if (argc > 0)
- reject(u"DefineArray"_qs);
+ INJECT_TRACE_INFO(generate_DefineArray);
- m_body += m_state.accumulatorVariableOut + u" = "_qs;
- m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(),
- QString());
- m_body += u";\n"_qs;
- generateOutputVariantConversion(m_typeResolver->emptyListType());
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
+
+ if (argc == 0) {
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->emptyListType(), stored, QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->emptyListType());
+ return;
+ }
+
+ if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
+ // This rejects any attempt to store the list into a QVariant.
+ // Therefore, we don't have to adjust the contained type below.
+ reject(u"storing an array in a non-sequence type"_qs);
+ return;
+ }
+
+ const QQmlJSScope::ConstPtr value = stored->valueType();
+ Q_ASSERT(value);
+
+ QStringList initializer;
+ for (int i = 0; i < argc; ++i) {
+ initializer += conversion(registerType(args + i).storedType(), value,
+ registerVariable(args + i));
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs + stored->internalName() + u'{';
+ m_body += initializer.join(u", "_qs);
+ m_body += u"};\n";
}
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index c993197399..ce8df3148f 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -188,7 +188,7 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
const auto property = m_objectType->property(propertyName);
function.returnType = property.isList()
- ? m_typeResolver->listType(property.type())
+ ? m_typeResolver->listType(property.type(), QQmlJSTypeResolver::UseQObjectList)
: QQmlJSScope::ConstPtr(property.type());
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 1e48a615f6..fbcdccafaf 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -1412,10 +1412,15 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(args);
setAccumulator(m_typeResolver->globalType(argc == 0
? m_typeResolver->emptyListType()
- : m_typeResolver->jsValueType()));
+ : m_typeResolver->variantListType()));
+
+ // Track all arguments as the same type.
+ const QQmlJSRegisterContent elementType
+ = m_typeResolver->tracked(m_typeResolver->globalType(m_typeResolver->varType()));
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(args + i, elementType);
}
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 1b0520e70d..766e8513db 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -96,6 +96,8 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
listPropertyType->setInternalName(u"QQmlListProperty<QObject>"_qs);
listPropertyType->setFilePath(u"qqmllist.h"_qs);
listPropertyType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
+ listPropertyType->setValueTypeName(u"QObject"_qs);
+ QQmlJSScope::resolveTypes(listPropertyType, builtinTypes);
m_listPropertyType = listPropertyType;
QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create();
@@ -177,7 +179,8 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
return m_objectsById.scope(id, referrer);
}
-QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &elementType) const
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(
+ const QQmlJSScope::ConstPtr &elementType, ListMode mode) const
{
auto it = m_typeTracker->listTypes.find(elementType);
if (it != m_typeTracker->listTypes.end())
@@ -185,12 +188,16 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &
switch (elementType->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference:
- return m_listPropertyType;
+ if (mode == UseListReference)
+ return m_listPropertyType;
+ if (elementType->internalName() != u"QObject"_qs)
+ return listType(genericType(elementType), mode);
+ Q_FALLTHROUGH();
case QQmlJSScope::AccessSemantics::Value: {
QQmlJSScope::Ptr listType = QQmlJSScope::create();
listType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
listType->setValueTypeName(elementType->internalName());
- listType->setInternalName(u"QList<%1>"_qs.arg(elementType->internalName()));
+ listType->setInternalName(u"QList<%1>"_qs.arg(elementType->augmentedInternalName()));
listType->setFilePath(elementType->filePath());
const QQmlJSImportedScope element = {elementType, QTypeRevision()};
QQmlJSScope::resolveTypes(listType, {{elementType->internalName(), element}});
@@ -344,7 +351,9 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
return container.type();
if (container.isProperty()) {
const QQmlJSMetaProperty prop = container.property();
- return prop.isList() ? listType(prop.type()) : QQmlJSScope::ConstPtr(prop.type());
+ return prop.isList()
+ ? listType(prop.type(), UseListReference)
+ : QQmlJSScope::ConstPtr(prop.type());
}
if (container.isEnumeration())
return container.enumeration().type();
@@ -709,7 +718,9 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
return m_intType;
if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
- return listType(genericType(type->valueType()));
+ return equals(type, m_listPropertyType)
+ ? type
+ : listType(genericType(type->valueType()), UseQObjectList);
}
return m_varType;
@@ -977,7 +988,7 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (equals(to, m_jsPrimitiveType))
return isPrimitive(from);
- if (equals(from, m_emptyListType))
+ if (equals(from, m_emptyListType) || equals(from, m_variantListType))
return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
const bool matchByName = !to->isComposite();
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 32c64b89f7..4426a31f86 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -89,7 +89,8 @@ public:
return m_imports.contains(name) && !m_imports[name].scope;
}
- QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType) const;
+ enum ListMode { UseListReference, UseQObjectList };
+ QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType, ListMode mode) const;
QQmlJSScope::ConstPtr typeForName(const QString &name) const { return m_imports[name].scope; }
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
diff --git a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml
index 0072357ae3..5ec54572a9 100644
--- a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml
@@ -2,6 +2,12 @@ pragma Strict
import QtQml
QtObject {
+ id: self
+ property date aDate
property list<bool> values1: []
property list<int> values2: []
+ property list<int> values3: [1, 2, 3, 4]
+ property list<date> values4: [aDate, aDate, aDate]
+ property list<real> values5: [1, 2, 3.4, "30", undefined, null]
+ property list<QtObject> values6: [self, self, self]
}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 74794eb7fc..e8861b3b84 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -126,6 +126,7 @@ private slots:
void functionTakingVar();
void testIsnan();
void fallbackLookups();
+ void typedArray();
};
void tst_QmlCppCodegen::simpleBinding()
@@ -1904,6 +1905,34 @@ void tst_QmlCppCodegen::fallbackLookups()
QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
}
+void tst_QmlCppCodegen::typedArray()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/typedArray.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QDateTime date;
+ QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty());
+ QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")),
+ QList<int>({1, 2, 3, 4}));
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
+ QCOMPARE(qvariant_cast<QList<double>>(o->property("values5")),
+ QList<double>({1, 2, 3.4, 30, 0, 0}));
+ date = QDateTime::currentDateTime();
+ o->setProperty("aDate", date);
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
+
+ QQmlListProperty<QObject> values6
+ = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6"));
+ QCOMPARE(values6.count(&values6), 3);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values6.at(&values6, i), o.data());
+}
+
void tst_QmlCppCodegen::runInterpreted()
{
if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))