aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-04-17 14:12:05 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-04-25 16:53:00 +0200
commit365b781599993aef933228599eaeb6eb909d9a93 (patch)
tree5c8ef6bd3a8908d629e0939516a3c56efb7d1c18
parentd664cb4845f8b5c79ce34bb963312b18cce7452f (diff)
QmlCompiler: Implement ConvertThisToObject and basic DTZ
We know that 'this' is a QObject* since the metatypes stack frame mandates it. Whenever you pass 'this' to anything it's loaded from the special 'This' stack slot which then triggers a DTZ check. A DTZ check is a noop if we can prove that the type is statically known, though. In QmlCompiler, if we have a valid register content, then the register has been set in all code paths that lead to the instruction in question. Fixes: QTBUG-111439 Change-Id: I81d1cd140eea63f85628c3bef3a8f6db0a12096d Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/qml/qqml.cpp6
-rw-r--r--src/qml/qml/qqmlprivate.h1
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp9
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h1
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp27
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp1
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/thisObject.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp10
11 files changed, 74 insertions, 9 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index d258ace774..7bbfe39ae9 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -804,6 +804,12 @@ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
qmlregister(TypeAndRevisionsRegistration, &type);
}
+QObject *AOTCompiledContext::thisObject() const
+{
+ return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
+ ->thisObject();
+}
+
QQmlEngine *AOTCompiledContext::qmlEngine() const
{
return qmlContext ? qmlContext->engine() : nullptr;
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 483b6aaceb..14e4e3ef98 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -623,6 +623,7 @@ namespace QQmlPrivate
qintptr extraData;
};
+ QObject *thisObject() const;
QQmlEngine *qmlEngine() const;
QJSValue jsMetaType(int index) const;
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index dd2a32c474..327aeabfd9 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -175,7 +175,9 @@ QT_WARNING_POP
else
result.code += u' ';
- if (!registerIsArgument && registerIndex != Accumulator
+ if (!registerIsArgument
+ && registerIndex != Accumulator
+ && registerIndex != This
&& !m_typeResolver->registerIsStoredIn(
function->registerTypes[registerIndex - firstRegisterIndex()],
m_typeResolver->voidType())) {
@@ -1902,7 +1904,8 @@ void QQmlJSCodeGenerator::generate_UnwindToLabel(int level, int offset)
void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(int name)
{
Q_UNUSED(name)
- BYTECODE_UNIMPLEMENTED();
+ // Nothing to do here. If we have statically asserted the dtz check in the type propagator
+ // the value cannot be empty. Otherwise we can't get here.
}
void QQmlJSCodeGenerator::generate_ThrowException()
@@ -2121,7 +2124,7 @@ void QQmlJSCodeGenerator::generate_CreateRestParameter(int argIndex)
void QQmlJSCodeGenerator::generate_ConvertThisToObject()
{
- BYTECODE_UNIMPLEMENTED();
+ m_body += changedRegisterVariable() + u" = aotContext->thisObject();\n"_s;
}
void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index a00f580065..9186dd5986 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -32,6 +32,7 @@ public:
enum RegisterShortcuts {
InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
+ This = QV4::CallData::This,
FirstArgument = QV4::CallData::OffsetCount
};
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 650fdeb191..aa1b6880ec 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -1520,8 +1520,22 @@ void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
{
- Q_UNUSED(name)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ const auto fail = [this, name]() {
+ setError(u"Cannot statically assert the dead temporal zone check for %1"_s.arg(
+ name ? m_jsUnitGenerator->stringForIndex(name) : u"the anonymous accumulator"_s));
+ };
+
+ const QQmlJSRegisterContent in = m_state.accumulatorIn();
+ if (in.isConversion()) {
+ for (const QQmlJSScope::ConstPtr &origin : in.conversionOrigins()) {
+ if (!m_typeResolver->equals(origin, m_typeResolver->emptyType()))
+ continue;
+ fail();
+ break;
+ }
+ } else if (m_typeResolver->registerContains(in, m_typeResolver->emptyType())) {
+ fail();
+ }
}
void QQmlJSTypePropagator::generate_ThrowException()
@@ -1703,7 +1717,7 @@ void QQmlJSTypePropagator::generate_CreateRestParameter(int argIndex)
void QQmlJSTypePropagator::generate_ConvertThisToObject()
{
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ setRegister(This, m_typeResolver->globalType(m_typeResolver->qObjectType()));
}
void QQmlJSTypePropagator::generate_LoadSuperConstructor()
@@ -2161,9 +2175,8 @@ void QQmlJSTypePropagator::generate_Sub(int lhs)
void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
- Q_UNUSED(firstReg)
- Q_UNUSED(count)
- // Ignore. We reject uninitialized values anyway.
+ for (int reg = firstReg, end = firstReg + count; reg < end; ++reg)
+ setRegister(reg, m_typeResolver->globalType(m_typeResolver->emptyType()));
}
void QQmlJSTypePropagator::generate_ThrowOnNullOrUndefined()
@@ -2270,6 +2283,8 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
+ case QV4::Moth::Instr::Type::ConvertThisToObject:
+ case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
setError(u"Instruction is not expected to populate the accumulator"_s);
return;
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 425f6320b1..3c4076b0a4 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -49,6 +49,7 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_varType = builtinTypes.type(u"QVariant"_s).scope;
m_jsValueType = builtinTypes.type(u"QJSValue"_s).scope;
m_listPropertyType = builtinTypes.type(u"QQmlListProperty<QObject>"_s).scope;
+ m_qObjectType = builtinTypes.type(u"QObject"_s).scope;
m_qObjectListType = builtinTypes.type(u"QObjectList"_s).scope;
QQmlJSScope::Ptr emptyType = QQmlJSScope::create();
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 5cd698fbdb..feeac52f6d 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -70,6 +70,7 @@ public:
QQmlJSScope::ConstPtr metaObjectType() const { return m_metaObjectType; }
QQmlJSScope::ConstPtr functionType() const { return m_functionType; }
QQmlJSScope::ConstPtr jsGlobalObject() const { return m_jsGlobalObject; }
+ QQmlJSScope::ConstPtr qObjectType() const { return m_qObjectType; }
QQmlJSScope::ConstPtr qObjectListType() const { return m_qObjectListType; }
QQmlJSScope::ConstPtr scopeForLocation(const QV4::CompiledData::Location &location) const;
@@ -236,6 +237,7 @@ protected:
QQmlJSScope::ConstPtr m_jsValueType;
QQmlJSScope::ConstPtr m_jsPrimitiveType;
QQmlJSScope::ConstPtr m_listPropertyType;
+ QQmlJSScope::ConstPtr m_qObjectType;
QQmlJSScope::ConstPtr m_qObjectListType;
QQmlJSScope::ConstPtr m_metaObjectType;
QQmlJSScope::ConstPtr m_functionType;
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 01d02c03f9..7ed6e8f624 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -192,6 +192,7 @@ set(qml_files
text.qml
themerbad.qml
themergood.qml
+ thisObject.qml
throwObjectName.qml
toString.qml
translation.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml
index e802a43302..2ff520cd73 100644
--- a/tests/auto/qml/qmlcppcodegen/data/failures.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml
@@ -70,4 +70,18 @@ QtObject {
function readTracks(metadataList : list<badType>): int {
return metadataList.length
}
+
+ function dtzFail() : int {
+ for (var a = 10; a < 20; ++a) {
+ switch (a) {
+ case 11:
+ let b = 5;
+ break;
+ case 10:
+ console.log(b);
+ break;
+ }
+ }
+ return a;
+ }
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/thisObject.qml b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
new file mode 100644
index 0000000000..50664eced2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject warned
+
+ function f(arg: QtObject) { warned = arg }
+ function warn() { f(this) }
+
+ Component.onCompleted: warn()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 14cdee5501..6131224ef0 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -179,6 +179,7 @@ private slots:
void boolPointerMerge();
void mergedObjectReadWrite();
void listConversion();
+ void thisObject();
};
void tst_QmlCppCodegen::initTestCase()
@@ -3611,7 +3612,16 @@ void tst_QmlCppCodegen::listConversion()
QVariant::fromValue<qsizetype>(3),
QVariant::fromValue<Person *>(nullptr)
}));
+}
+void tst_QmlCppCodegen::thisObject()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/thisObject.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("warned").value<QObject *>(), o.data());
}
QTEST_MAIN(tst_QmlCppCodegen)