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.

Pick-to: 6.4
Fixes: QTBUG-108634
Change-Id: I6a5323c2e0c023838609aec90d7ecc15b885dc08
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2022-11-18 11:40:10 +01:00
parent dca259f040
commit bce216d5c0
4 changed files with 64 additions and 11 deletions

View File

@ -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)
@ -2459,9 +2460,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(),

View File

@ -125,6 +125,7 @@ set(qml_files
notEqualsInt.qml
notNotString.qml
nullAccess.qml
nullComparison.qml
objectInVar.qml
outOfBounds.qml
overriddenMember.qml

View File

@ -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;
}
}
}

View File

@ -145,6 +145,7 @@ private slots:
void letAndConst();
void signalIndexMismatch();
void callWithSpread();
void nullComparison();
};
void tst_QmlCppCodegen::initTestCase()
@ -2802,6 +2803,21 @@ void tst_QmlCppCodegen::callWithSpread()
QTest::ignoreMessage(QtCriticalMsg, "That is great!");
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
}
void tst_QmlCppCodegen::nullComparison()
{
QQmlEngine engine;
QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/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)