diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-04-17 14:12:05 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-04-25 16:53:00 +0200 |
commit | 365b781599993aef933228599eaeb6eb909d9a93 (patch) | |
tree | 5c8ef6bd3a8908d629e0939516a3c56efb7d1c18 | |
parent | d664cb4845f8b5c79ce34bb963312b18cce7452f (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.cpp | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlprivate.h | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 9 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscompilepass_p.h | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 27 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/failures.qml | 14 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/thisObject.qml | 11 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 10 |
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) |