From 6ff6c088c22938fc35690d385a9625f33c18a35a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 19 May 2022 16:38:55 +0200 Subject: QmlCompiler: Add support for LoadElement on strings In JavaScript the [] operator on strings returns a string. QString's operator[] returns a QChar, but we can easily create a string from that. Fixes: QTBUG-103371 Change-Id: Id5c960f00ecc7a5dfe30ccbcaac3ffb2a30308b9 Reviewed-by: Fabian Kosmale --- src/qmlcompiler/qqmljscodegenerator.cpp | 15 +++++++++++---- src/qmlcompiler/qqmljstypepropagator.cpp | 3 ++- src/qmlcompiler/qqmljstyperesolver.cpp | 2 ++ tests/auto/qml/qmlcppcodegen/data/typedArray.qml | 9 +++++++++ tests/auto/qml/qmlcppcodegen/data/valueTypeLists.qml | 3 +++ tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 18 ++++++++++++++++++ 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index 8568154e3c..8c89745a37 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -622,7 +622,9 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) const QQmlJSRegisterContent baseType = registerType(base); - if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) { + if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) + || (!baseType.isList() + && !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType()))) { reject(u"LoadElement with non-list base type or non-numeric arguments"_s); return; } @@ -662,11 +664,16 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) const auto elementType = m_typeResolver->valueType(baseType); + QString access = baseName + u".at("_s + indexName + u')'; + + // TODO: Once we get a char type in QML, use it here. + if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType())) + access = u"QString("_s + access + u")"_s; + m_body += u"if ("_s + indexName + u" >= 0 && "_s + indexName - + u" < "_s + baseName + u".count())\n"_s; + + u" < "_s + baseName + u".size())\n"_s; m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s + - conversion(elementType, m_state.accumulatorOut(), - baseName + u".at("_s + indexName + u')') + u";\n"_s; + conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s; m_body += u"else\n"_s + voidAssignment; } diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 7f859061fa..27e120a832 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -652,7 +652,8 @@ void QQmlJSTypePropagator::generate_LoadElement(int base) { const QQmlJSRegisterContent baseRegister = m_state.registers[base]; - if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence + if ((baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence + && !m_typeResolver->registerIsStoredIn(baseRegister, m_typeResolver->stringType())) || !m_typeResolver->isNumeric(m_state.accumulatorIn())) { const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType()); addReadAccumulator(jsValue); diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index c01cf5dfb3..623772027f 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -1178,6 +1178,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent return scope->valueType(); else if (equals(scope, m_jsValueType) || equals(scope, m_varType)) return m_jsValueType; + else if (equals(scope, m_stringType)) + return m_stringType; return QQmlJSScope::ConstPtr(); }; diff --git a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml index f9a40f5584..8d94d0832f 100644 --- a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml +++ b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml @@ -10,8 +10,17 @@ QtObject { property list values4: [aDate, aDate, aDate] property list values5: [1, 2, 3.4, "30", undefined, null] property list values6: [self, self, self] + property string values7: "abcdef" property int inIntList: values3[1] property date inDateList: values4[2] property real inRealList: values5[3] + property string inCharList: values7[5] + + function stringAt10(s: string): int { + if (!s[10]) + return 10; + else + return 20; + } } diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeLists.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeLists.qml index 83325641ab..cf58ae68c5 100644 --- a/tests/auto/qml/qmlcppcodegen/data/valueTypeLists.qml +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeLists.qml @@ -4,6 +4,7 @@ QtObject { property list rectList: [ Qt.rect(1,2,3,4), Qt.rect(5,6,7,8), Qt.rect(9,10,11,12) ] property list stringList: ["aaa", "bbb", "ccc"] property list intList: [5, 6, 7, 8] + property string charList: "abcde" property var rectInBounds: rectList[0] property var rectOutOfBounds: rectList[32] @@ -11,4 +12,6 @@ QtObject { property var stringOutOfBounds: stringList[33] property var intInBounds: intList[2] property var intOutOfBounds: intList[34] + property var charInBounds: charList[3] + property var charOutOfBounds: charList[35] } diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index b4993d1752..84c5ad2deb 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -1955,6 +1955,20 @@ void tst_QmlCppCodegen::typedArray() QCOMPARE(o->property("inIntList").toInt(), 2); QCOMPARE(qvariant_cast(o->property("inDateList")), date); QCOMPARE(o->property("inRealList").toDouble(), 30.0); + QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f")); + + const QMetaObject *metaObject = o->metaObject(); + QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)")); + QVERIFY(method.isValid()); + + // If LoadElement threw an exception the function would certainly return neither 10 nor 20. + int result = 0; + method.invoke( + o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a"))); + QCOMPARE(result, 10); + method.invoke( + o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa"))); + QCOMPARE(result, 20); } void tst_QmlCppCodegen::prefixedType() @@ -2126,6 +2140,10 @@ void tst_QmlCppCodegen::valueTypeLists() QCOMPARE(qvariant_cast(o->property("intInBounds")), 7); QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0); QVERIFY(!o->property("intOutOfBounds").isValid()); + + QCOMPARE(qvariant_cast(o->property("charInBounds")), QStringLiteral("d")); + QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0); + QVERIFY(!o->property("charOutOfBounds").isValid()); } void tst_QmlCppCodegen::boundComponents() -- cgit v1.2.3