diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2022-07-27 17:03:44 +0200 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2022-07-29 15:23:00 +0200 |
commit | f9781aca9d1343521c00fdf74bda8638e0a45db4 (patch) | |
tree | 07f0b873405bdd7e83c8a739bd17f84992e6cd4e | |
parent | 2130353ba487fd1e54af3815b11c43858ea807f7 (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.cpp | 1 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 23 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.h | 11 | ||||
-rw-r--r-- | tools/qmltc/qmltccompilerpieces.h | 32 | ||||
-rw-r--r-- | tools/qmltc/qmltcoutputir.h | 6 |
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 ¤t, 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 ¤t, } QmltcCodeGenerator::generate_createBindingOnProperty( - ¤t.endInit.body, generate_callCompilationUnit(m_urlMethodName), + ¤t.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 ¤t, // 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 ¤t, const QQmlJSScope::ConstPtr &type) const; + inline void generate_setComplexBindingsCode(QmltcType ¤t, + 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 ¤t, .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 ¤t, /*! \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 ¤t, + 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(¤t.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 |