aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2025-06-17 14:15:02 +0200
committerUlf Hermann <[email protected]>2025-06-23 11:17:06 +0200
commit9035d1cb2a474a6df52ca395e6c185964ec94de0 (patch)
tree7a6181b7422b1b42c68901890f9aad4f5b3900bc
parentb303e0624d2ea2ab1c124961e7510a64d0ca1412 (diff)
QmlCompiler: Restore support for writing to temporary arraysHEADdev
We use the JavaScript extension as "read" type to signify that the code generator will accept any type with such an extension. This requires some adjustments in the type resolver. Pick-to: 6.10 6.9 6.8 6.5 Task-number: QTBUG-137540 Change-Id: Ia34ca0a24c417c5372852268ba2f55825484639d Reviewed-by: Olivier De Cannière <[email protected]>
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp10
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h2
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp9
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp107
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Categorizer.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp15
7 files changed, 114 insertions, 40 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 6a730f8ef7..3c8a5dcaf2 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -1159,6 +1159,16 @@ void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
if (writeBack.isConversion())
REJECT(u"write-back of converted value"_s);
+ switch (writeBack.variant()) {
+ case QQmlJSRegisterContent::Literal:
+ case QQmlJSRegisterContent::Operation:
+ // If the value type or list was produced as a literal or as result
+ // of an operation (like DefineArray ...), we don't have to write back.
+ return;
+ default:
+ break;
+ }
+
const int lookupIndex = writeBack.resultLookupIndex();
// This is essential for the soundness of the type system.
diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h
index ef1e11affd..fd94811f1d 100644
--- a/src/qmlcompiler/qqmljsregistercontent_p.h
+++ b/src/qmlcompiler/qqmljsregistercontent_p.h
@@ -64,7 +64,7 @@ public:
// Either a synthetic type or a merger of multiple different variants.
// In the latter case, look at conversion origins to find out more.
// Synthetic types should be short lived.
- Unknown,the
+ Unknown,
};
enum { InvalidLookupIndex = -1 };
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 56f1a2fd78..276e7a03c2 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -789,11 +789,14 @@ void QQmlJSTypePropagator::generate_LoadElement(int base)
m_typeResolver->convert(m_typeResolver->valueType(baseRegister), jsValue)));
};
- if (!baseRegister.isList() && !baseRegister.contains(m_typeResolver->stringType())) {
+ if (baseRegister.isList()) {
+ addReadRegister(base, m_typeResolver->arrayPrototype());
+ } else if (baseRegister.contains(m_typeResolver->stringType())) {
+ addReadRegister(base, m_typeResolver->stringType());
+ } else {
fallback();
return;
}
- addReadRegister(base);
if (m_typeResolver->isNumeric(in)) {
const auto contained = in.containedType();
@@ -842,7 +845,7 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
else
addReadRegister(index, m_typeResolver->realType());
- addReadRegister(base);
+ addReadRegister(base, m_typeResolver->arrayPrototype());
addReadAccumulator(m_typeResolver->valueType(baseRegister));
// If we're writing a QQmlListProperty backed by a container somewhere else,
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 32c7db5e3e..17b5b42972 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -533,16 +533,28 @@ bool QQmlJSTypeResolver::adjustTrackedType(
return true;
QQmlJSScope::ConstPtr contained = tracked.containedType();
+ QQmlJSScope::ConstPtr result = conversion;
+
+ // Do not adjust to the JavaScript extension of the original type. Rather keep the original
+ // type in that case.
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ contained, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
+ if (kind != QQmlJSScope::ExtensionJavaScript || scope != result)
+ return false;
+ result = contained;
+ return true;
+ });
+
// If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
// we better not change the type.
- if (!canPrimitivelyConvertFromTo(contained, conversion)
- && !canPopulate(conversion, contained, nullptr)
- && !selectConstructor(conversion, contained, nullptr).isValid()) {
+ if (!canPrimitivelyConvertFromTo(contained, result)
+ && !canPopulate(result, contained, nullptr)
+ && !selectConstructor(result, contained, nullptr).isValid()) {
return false;
}
- m_pool->adjustType(tracked, conversion);
+ m_pool->adjustType(tracked, result);
return true;
}
@@ -562,18 +574,7 @@ bool QQmlJSTypeResolver::adjustTrackedType(
for (QQmlJSRegisterContent type : conversions)
result = merge(type.containedType(), result);
- QQmlJSScope::ConstPtr contained = tracked.containedType();
-
- // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
- // we better not change the type.
- if (!canPrimitivelyConvertFromTo(contained, result)
- && !canPopulate(result, contained, nullptr)
- && !selectConstructor(result, contained, nullptr).isValid()) {
- return false;
- }
-
- m_pool->adjustType(tracked, result);
- return true;
+ return adjustTrackedType(tracked, result);
}
void QQmlJSTypeResolver::adjustOriginalType(
@@ -691,19 +692,37 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (b.isNull())
return a;
- const auto commonBaseType = [](
- const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
- for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
- for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
- if (aBase == bBase)
- return aBase;
+ const auto baseOrExtension
+ = [](const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
+ QQmlJSScope::ConstPtr found;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ a, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
+ switch (kind) {
+ case QQmlJSScope::NotExtension:
+ // If b inherits scope, then scope is a common base type of a and b
+ if (b->inherits(scope)) {
+ found = scope;
+ return true;
+ }
+ break;
+ case QQmlJSScope::ExtensionJavaScript:
+ // Merging a type with its JavaScript extension produces the type.
+ // Giving the JavaScript extension as type to be read means we expect any type
+ // that fulfills the given JavaScript interface
+ if (scope == b) {
+ found = a;
+ return true;
+ }
+ break;
+ case QQmlJSScope::ExtensionType:
+ case QQmlJSScope::ExtensionNamespace:
+ break;
}
- }
-
- return QQmlJSScope::ConstPtr();
+ return false;
+ });
+ return found;
};
-
if (a == b)
return a;
@@ -736,8 +755,11 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (isPrimitive(a) && isPrimitive(b))
return jsPrimitiveType();
- if (auto commonBase = commonBaseType(a, b))
- return commonBase;
+ if (const auto base = baseOrExtension(a, b))
+ return base;
+
+ if (const auto base = baseOrExtension(b, a))
+ return base;
if ((a == nullType() || a == boolType()) && b->isReferenceType())
return b;
@@ -1459,18 +1481,31 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (to == m_jsPrimitiveType)
return isPrimitive(from);
- if (from == m_variantListType)
- return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
-
const bool matchByName = !to->isComposite();
Q_ASSERT(!matchByName || !to->internalName().isEmpty());
- for (auto baseType = from; baseType; baseType = baseType->baseType()) {
- if (baseType == to)
- return true;
- if (matchByName && baseType->internalName() == to->internalName())
- return true;
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(
+ from, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
+ switch (kind) {
+ case QQmlJSScope::NotExtension:
+ case QQmlJSScope::ExtensionJavaScript:
+ // Converting to a base type is trivially supported.
+ // Converting to the JavaScript extension of a type just produces the type itself.
+ // Giving the JavaScript extension as type to be converted to means we expect any
+ // result that fulfills the given JavaScript interface.
+ return scope == to
+ || (matchByName && scope->internalName() == to->internalName());
+ case QQmlJSScope::ExtensionType:
+ case QQmlJSScope::ExtensionNamespace:
+ break;
+ }
+ return false;
+ })) {
+ return true;
}
+ if (from == m_variantListType)
+ return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
+
// We can convert anything that fits into QJSPrimitiveValue
if (canConvertFromTo(from, m_jsPrimitiveType) && canConvertFromTo(m_jsPrimitiveType, to))
return true;
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index bd00605eb2..36fb7ab01b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -50,6 +50,7 @@ set(qml_files
BaseMember.qml
BindingExpression.qml
BindingToScriptStringProperty.qml
+ Categorizer.qml
CxxTypeFromDir.qml
CxxTypeFromImplicit.qml
Cycle1.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml b/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml
new file mode 100644
index 0000000000..10f2857c19
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Categorizer.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<double> nnn: {
+ var result = [];
+ result[0] = 10;
+ return result;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index ffd18f09a7..b322f870fa 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -288,6 +288,7 @@ private slots:
void voidFunction();
void writeBack();
void writeVariantMap();
+ void writeAndReturnTempArray();
};
static QByteArray arg1()
@@ -5838,6 +5839,20 @@ void tst_QmlCppCodegen::writeVariantMap()
}
+void tst_QmlCppCodegen::writeAndReturnTempArray()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Categorizer.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const QVariant nnn = object->property("nnn");
+ QCOMPARE(nnn.metaType(), QMetaType::fromType<QList<double>>());
+ QCOMPARE(nnn.value<QList<double>>(), QList<double>{10});
+}
+
QTEST_MAIN(tst_QmlCppCodegen)
#include "tst_qmlcppcodegen.moc"