aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2022-07-27 17:03:44 +0200
committerAndrei Golubev <andrei.golubev@qt.io>2022-07-29 15:23:00 +0200
commitf9781aca9d1343521c00fdf74bda8638e0a45db4 (patch)
tree07f0b873405bdd7e83c8a739bd17f84992e6cd4e
parent2130353ba487fd1e54af3815b11c43858ea807f7 (diff)
Separate script bindings from others in generated qmltc code
Align script binding creation to QQmlObjectCreator model: simple bindings (e.g. literal, object, etc.) are created instantly when encountered; complex bindings (e.g. script, translation?) are posponed until QQmlObjectCreator::finalize() (in reality it is a bit more complicated). Make qmltc follow this logic by creating script bindings in a separate object creation step Additionally, this should theoretically improve performance of the object creation process as script bindings (that have interdependencies) would now re-trigger less as they are all centrally created at once (after simple bindings are already done). Note, however, that the specific order of binding creation between children may vary in comparison to QQmlObjectCreator Task-number: QTBUG-91956 Task-number: QTBUG-96269 Change-Id: I25067adf7b16d0f1852a5108a0b6d6597ac28a53 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 570497088513ab284d9b65f5e7d2b6888a424b87) Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--tools/qmltc/qmltccodewriter.cpp1
-rw-r--r--tools/qmltc/qmltccompiler.cpp23
-rw-r--r--tools/qmltc/qmltccompiler.h11
-rw-r--r--tools/qmltc/qmltccompilerpieces.h32
-rw-r--r--tools/qmltc/qmltcoutputir.h6
5 files changed, 60 insertions, 13 deletions
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index afdfd78d7e..7c9958e215 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -323,6 +323,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
QmltcCodeWriter::write(code, type.baselineCtor);
QmltcCodeWriter::write(code, type.init);
QmltcCodeWriter::write(code, type.endInit);
+ QmltcCodeWriter::write(code, type.setComplexBindings);
QmltcCodeWriter::write(code, type.beginClass);
QmltcCodeWriter::write(code, type.completeComponent);
QmltcCodeWriter::write(code, type.finalizeComponent);
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 9a38cca06c..31140e5281 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -188,6 +188,7 @@ void QmltcCompiler::compileType(
current.init.access = QQmlJSMetaMethod::Protected;
current.beginClass.access = QQmlJSMetaMethod::Protected;
current.endInit.access = QQmlJSMetaMethod::Protected;
+ current.setComplexBindings.access = QQmlJSMetaMethod::Protected;
current.completeComponent.access = QQmlJSMetaMethod::Protected;
current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
@@ -200,6 +201,8 @@ void QmltcCompiler::compileType(
current.beginClass.returnType = u"void"_s;
current.endInit.name = u"QML_endInit"_s;
current.endInit.returnType = u"void"_s;
+ current.setComplexBindings.name = u"QML_setComplexBindings"_s;
+ current.setComplexBindings.returnType = u"void"_s;
current.completeComponent.name = u"QML_completeComponent"_s;
current.completeComponent.returnType = u"void"_s;
current.finalizeComponent.name = u"QML_finalizeComponent"_s;
@@ -217,6 +220,7 @@ void QmltcCompiler::compileType(
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
current.beginClass.parameterList = { creator, finalizeFlag };
current.endInit.parameterList = { creator, engine, finalizeFlag };
+ current.setComplexBindings.parameterList = { creator, engine, finalizeFlag };
current.completeComponent.parameterList = { creator, finalizeFlag };
current.finalizeComponent.parameterList = { creator, finalizeFlag };
current.handleOnCompleted.parameterList = { creator, finalizeFlag };
@@ -225,6 +229,7 @@ void QmltcCompiler::compileType(
current.init.parameterList = { creator, engine, ctxtdata };
current.beginClass.parameterList = { creator };
current.endInit.parameterList = { creator, engine };
+ current.setComplexBindings.parameterList = { creator, engine };
current.completeComponent.parameterList = { creator };
current.finalizeComponent.parameterList = { creator };
current.handleOnCompleted.parameterList = { creator };
@@ -266,6 +271,7 @@ void QmltcCompiler::compileType(
auto postponedQmlContextSetup = generator.generate_initCode(current, type);
generator.generate_endInitCode(current, type);
+ generator.generate_setComplexBindingsCode(current, type);
generator.generate_beginClassCode(current, type);
generator.generate_completeComponentCode(current, type);
generator.generate_finalizeComponentCode(current, type);
@@ -281,9 +287,8 @@ static Iterator partitionBindings(Iterator first, Iterator last)
// later point, so we should sort or partition the range. we do a stable
// partition since the relative order of binding evaluation affects the UI
return std::stable_partition(first, last, [](const QQmlJSMetaPropertyBinding &b) {
- // we want script bindings to be at the end, so do the negation of "is
- // script binding"
- return b.bindingType() != QQmlJSMetaPropertyBinding::Script;
+ // we want complex bindings to be at the end, so do the negation
+ return !QmltcCompiler::isComplexBinding(b);
});
}
@@ -1227,10 +1232,10 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
slotMethod.type = QQmlJSMetaMethod::Slot;
current.functions << std::move(slotMethod);
- current.endInit.body << u"QObject::connect(" + This_signal + u", "
- + QmltcCodeGenerator::wrap_qOverload(slotParameters,
- u"&" + objectClassName_signal + u"::"
- + signalName)
+ current.setComplexBindings.body
+ << u"QObject::connect(" + This_signal + u", "
+ + QmltcCodeGenerator::wrap_qOverload(
+ slotParameters, u"&" + objectClassName_signal + u"::" + signalName)
+ u", " + This_slot + u", &" + objectClassName_slot + u"::" + slotName
+ u");";
};
@@ -1269,7 +1274,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
}
QmltcCodeGenerator::generate_createBindingOnProperty(
- &current.endInit.body, generate_callCompilationUnit(m_urlMethodName),
+ &current.setComplexBindings.body, generate_callCompilationUnit(m_urlMethodName),
u"this"_s, // NB: always using enclosing object as a scope for the binding
static_cast<qsizetype>(objectType->ownRuntimeFunctionIndex(binding.scriptIndex())),
bindingTarget, // binding target
@@ -1314,7 +1319,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
// TODO: this could be dropped if QQmlEngine::setContextForObject() is
// done before currently generated C++ object is constructed
- current.endInit.body << bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ current.setComplexBindings.body << bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ bindingFunctorName + u">("
+ QmltcCodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
+ u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index 67b21bad25..ede4d3c6a0 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -36,6 +36,17 @@ public:
~QmltcCompiler();
+ /*! \internal
+
+ Returns \c true if \a binding is considered complex by the compiler
+ (requires special code generation)
+ */
+ static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding)
+ {
+ // TODO: translation bindings (once supported) are also complex?
+ return binding.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ }
+
private:
QString m_url; // QML input file url
QmltcTypeResolver *m_typeResolver = nullptr;
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index ebdd5d048c..cbee19485d 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -40,6 +40,8 @@ struct QmltcCodeGenerator
const QString &baseInstructionArgs,
const QString &childInstructionArgs) const;
inline void generate_endInitCode(QmltcType &current, const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_setComplexBindingsCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
inline void generate_interfaceCallCode(QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
const QString &interfaceName,
@@ -251,6 +253,8 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
.arg(current.beginClass.name);
current.init.body << QStringLiteral(" %1(creator, engine, /* finalize */ true);")
.arg(current.endInit.name);
+ current.init.body << QStringLiteral(" %1(creator, engine, /* finalize */ true);")
+ .arg(current.setComplexBindings.name);
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
.arg(current.completeComponent.name);
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
@@ -398,6 +402,34 @@ inline void QmltcCodeGenerator::generate_endInitCode(QmltcType &current,
/*!
\internal
+ Generates \a{current.setComplexBindings}'s code. The setComplexBindings
+ method creates complex bindings (such as script bindings). Additionally, the
+ QML document root's setComplexBindings calls setComplexBindings methods of
+ all the necessary QML types within the document.
+*/
+inline void
+QmltcCodeGenerator::generate_setComplexBindingsCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+
+ // QML_setComplexBindings()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * QQmlEngine* engine
+ // * bool canFinalize [optional, when document root]
+ const bool isDocumentRoot = type == visitor->result();
+ current.setComplexBindings.body << u"Q_UNUSED(creator);"_s;
+ current.setComplexBindings.body << u"Q_UNUSED(engine);"_s;
+ if (isDocumentRoot)
+ current.setComplexBindings.body << u"Q_UNUSED(canFinalize);"_s;
+
+ generate_qmltcInstructionCallCode(&current.setComplexBindings, type,
+ u"engine, /* finalize */ false"_s, u"creator, engine"_s);
+}
+
+/*!
+ \internal
+
A generic helper function that generates interface code boilerplate, adding
it to a passed \a function. This is a building block used to generate e.g.
QQmlParserStatus API calls.
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index df4395232d..6e49636980 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -108,14 +108,12 @@ struct QmltcType
QmltcCtor externalCtor {}; // calls basicCtor, calls init
QmltcMethod init {}; // starts object initialization (context setup), calls finalize
QmltcMethod beginClass {}; // calls QQmlParserStatus::classBegin()
- QmltcMethod endInit {}; // ends object initialization (with binding setup)
+ QmltcMethod endInit {}; // ends object initialization (with "simple" bindings setup)
+ QmltcMethod setComplexBindings {}; // sets up "complex" (e.g. script) bindings
QmltcMethod completeComponent {}; // calls QQmlParserStatus::componentComplete()
QmltcMethod finalizeComponent {}; // calls QQmlFinalizerHook::componentFinalized()
QmltcMethod handleOnCompleted {}; // calls Component.onCompleted
- // TODO: add a separate special member function to set script bindings in a
- // separate step
-
std::optional<QmltcDtor> dtor {};
// member functions: methods, signals and slots