diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-11-18 11:40:10 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-11-24 13:07:04 +0100 |
commit | 39aba03d4c4bf80a54dbf5b6735b81854dd99220 (patch) | |
tree | aafa2b2ecb0f2b1e678a602fa08c726088876e74 | |
parent | 0fbb39435e04e02ecff4a95983be102d57d29e94 (diff) |
QmlCompiler: Fix comparison of null and undefined
Those are not stored. If we compare null to null or undefined to
undefined, we do not have to generate a comparison at all. the result is
statically known.
Fixes: QTBUG-108634
Change-Id: I6a5323c2e0c023838609aec90d7ecc15b885dc08
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit bce216d5c086a5aa8f88d13933eeccebca316359)
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 32 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/nullComparison.qml | 26 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 16 |
4 files changed, 64 insertions, 11 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index be0a937fc0..04926ca519 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -35,6 +35,15 @@ using namespace Qt::StringLiterals; m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \ } + +static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) +{ + return !type.isNull() + && !resolver->equals(type, resolver->nullType()) + && !resolver->equals(type, resolver->emptyListType()) + && !resolver->equals(type, resolver->voidType()); +} + QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const { return type->augmentedInternalName(); @@ -75,14 +84,6 @@ QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType) return QString(); } -static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) -{ - return !type.isNull() - && !resolver->equals(type, resolver->nullType()) - && !resolver->equals(type, resolver->emptyListType()) - && !resolver->equals(type, resolver->voidType()); -} - QQmlJSAotFunction QQmlJSCodeGenerator::run( const Function *function, const InstructionAnnotations *annotations, QQmlJS::DiagnosticMessage *error) @@ -2315,9 +2316,18 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func const auto primitive = m_typeResolver->jsPrimitiveType(); if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) { - m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), - registerVariable(lhs) + (invert ? u" != "_s : u" == "_s) - + m_state.accumulatorVariableIn); + if (isTypeStorable(m_typeResolver, lhsType)) { + m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), + registerVariable(lhs) + (invert ? u" != "_s : u" == "_s) + + m_state.accumulatorVariableIn); + } else if (m_typeResolver->equals(lhsType, m_typeResolver->emptyListType())) { + // We cannot compare two empty lists, because we don't know whether it's + // the same instance or not. "[] === []" is false, but "var a = []; a === a" is true; + reject(u"comparison of two empty lists"_s); + } else { + // null === null and undefined === undefined + m_body += invert ? u"false"_s : u"true"_s; + } } else { m_body += conversion( m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 8d062c5c35..2cd63be039 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -117,6 +117,7 @@ set(qml_files notEqualsInt.qml notNotString.qml nullAccess.qml + nullComparison.qml objectInVar.qml outOfBounds.qml overriddenMember.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml new file mode 100644 index 0000000000..1f9af7169b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml @@ -0,0 +1,26 @@ +pragma Strict +import QtQml + +QtObject { + property int v: 1 + property int w: 1 + property int x: 1 + property int y: 1 + Component.onCompleted: { + var g = null; + if (g !== null) { + v = 2; + } + if (g === null) { + w = 3; + } + + var h = undefined; + if (h !== undefined) { + x = 4; + } + if (h === undefined) { + y = 5; + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 74510d6097..063fede02e 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -134,6 +134,7 @@ private slots: void notNotString(); void inaccessibleProperty(); void typePropagationLoop(); + void nullComparison(); }; void tst_QmlCppCodegen::simpleBinding() @@ -2462,6 +2463,21 @@ void tst_QmlCppCodegen::typePropagationLoop() QCOMPARE(o->property("j").toInt(), 3); } +void tst_QmlCppCodegen::nullComparison() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/nullComparison.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("v").toInt(), 1); + QCOMPARE(o->property("w").toInt(), 3); + QCOMPARE(o->property("x").toInt(), 1); + QCOMPARE(o->property("y").toInt(), 5); +}; + QTEST_MAIN(tst_QmlCppCodegen) #include "tst_qmlcppcodegen.moc" |