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>
This commit is contained in:
parent
d664cb4845
commit
365b781599
|
@ -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;
|
||||
|
|
|
@ -623,6 +623,7 @@ namespace QQmlPrivate
|
|||
qintptr extraData;
|
||||
};
|
||||
|
||||
QObject *thisObject() const;
|
||||
QQmlEngine *qmlEngine() const;
|
||||
|
||||
QJSValue jsMetaType(int index) const;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
enum RegisterShortcuts {
|
||||
InvalidRegister = -1,
|
||||
Accumulator = QV4::CallData::Accumulator,
|
||||
This = QV4::CallData::This,
|
||||
FirstArgument = QV4::CallData::OffsetCount
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -192,6 +192,7 @@ set(qml_files
|
|||
text.qml
|
||||
themerbad.qml
|
||||
themergood.qml
|
||||
thisObject.qml
|
||||
throwObjectName.qml
|
||||
toString.qml
|
||||
translation.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue