diff options
author | Ulf Hermann <[email protected]> | 2025-06-17 14:15:02 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2025-06-23 11:17:06 +0200 |
commit | 9035d1cb2a474a6df52ca395e6c185964ec94de0 (patch) | |
tree | 7a6181b7422b1b42c68901890f9aad4f5b3900bc | |
parent | b303e0624d2ea2ab1c124961e7510a64d0ca1412 (diff) |
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.cpp | 10 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsregistercontent_p.h | 2 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 9 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 107 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/Categorizer.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 15 |
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" |