diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-10-22 12:02:37 +0200 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2021-11-15 12:00:55 +0100 |
commit | 0990b892cab819f210120f8a9f9ca48522bde412 (patch) | |
tree | 091eb6fd604649f996139986a2fb58d874bfa143 /tools/qmltc | |
parent | 539a08d1f4e4a672c33ccb21e4db5770491ebadb (diff) |
qmltc: Introduce object creation infrastructure
The so-far strategy is the following:
* allocate a linear storage for the QObject pointers
using QQmltcObjectCreationBase (to reference the
to-be-created objects)
* "type-erase" the QQmltcObjectCreationBase into
QQmltcObjectCreationHelper (so now we don't need to
know the template specialization across different
QML documents): non-document roots' ctors can accept
QQmltcObjectCreationHelper as an input parameter
* use QQmltcObjectCreationHelper to set object pointers
into the storage when creating all the objects (also
from other QML documents)
* use QQmltcObjectCreationHelper later to retrieve
necessary objects during creation finalization
Provide basic implementation for the first and second bullets
straight away
This model seems to allow to hide the details of QQmltcObjectCreationBase
interaction within the QML top-level document root (the type that we
instantiate in the user code), which is very convenient
Task-number: QTBUG-84368
Change-Id: I04cf64c54e48f169acfd4337cf93a58a05336b3b
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools/qmltc')
-rw-r--r-- | tools/qmltc/qmltccodewriter.cpp | 20 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 73 | ||||
-rw-r--r-- | tools/qmltc/qmltcoutputir.h | 6 |
3 files changed, 67 insertions, 32 deletions
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp index 3039b10da7..9670f64667 100644 --- a/tools/qmltc/qmltccodewriter.cpp +++ b/tools/qmltc/qmltccodewriter.cpp @@ -128,15 +128,20 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString for (const auto &requiredInclude : requiredCppIncludes) code.rawAppendToHeader(u"#include \"" + requiredInclude + u"\""); code.rawAppendToHeader(u"// END(custom_cpp_includes)"); + code.rawAppendToHeader(u"// qmltc support library:"); + code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>"); code.rawAppendToCpp(u"#include \"" + hPath + u"\""); // include own .h file code.rawAppendToCpp(u""); // blank line code.rawAppendToCpp(u"#include <private/qobject_p.h>"); // NB: for private properties + code.rawAppendToCpp(u"#include <private/qqmlglobal_p.h>"); // QQml_setParent_noEvent() code.rawAppendToHeader(u""); // blank line code.rawAppendToHeader(u"namespace q_qmltc {"); + code.rawAppendToCpp(u""); // blank line + code.rawAppendToCpp(u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE"); code.rawAppendToCpp(u"namespace q_qmltc {"); } @@ -263,17 +268,14 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type) } } - if (type.documentRootType) { - code.rawAppendToHeader(u""); // blank line - code.rawAppendToHeader(u"private:"); - code.rawAppendToHeader(u"friend class " + *type.documentRootType + u";", 1); - } - if (type.typeCount) { + // we know that typeCount variable is very special code.rawAppendToHeader(u""); // blank line - code.rawAppendToHeader(u"public:"); - code.rawAppendToHeader(u"constexpr static uint qmltc_typeCount = " - + QString::number(*type.typeCount) + u";", + code.rawAppendToHeader(u"protected:"); + Q_ASSERT(!type.typeCount->defaultValue.isEmpty()); + code.rawAppendToHeader(u"constexpr static %1 %2 = %3;"_qs.arg(type.typeCount->cppType, + type.typeCount->name, + type.typeCount->defaultValue), 1); } diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index e792e0787a..7ccc0e2eb6 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -84,33 +84,50 @@ void QmltcCompiler::compileType(QmltcType ¤t, const QQmlJSScope::ConstPtr Q_ASSERT(!type->baseType()->internalName().isEmpty()); const QString baseClass = type->baseType()->internalName(); - const bool documentRoot = (type == m_visitor->result()); + const auto rootType = m_visitor->result(); + const bool documentRoot = (type == rootType); const bool baseTypeIsCompiledQml = false; // TODO: support this in QmltcTypeResolver const bool isAnonymous = type->internalName().at(0).isLower(); current.baseClasses = { baseClass }; if (!documentRoot) { - current.documentRootType = m_visitor->result()->internalName(); + // make document root a friend to allow it to access init and finalize + current.otherCode << u"friend class %1;"_qs.arg(rootType->internalName()); } else { - current.typeCount = int(m_visitor->qmlScopes().size()); + // make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to + // be created for the root object + current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_qs.arg( + rootType->internalName()); + + current.typeCount = QmltcVariable { u"uint"_qs, u"q_qmltc_typeCount"_qs, QString() }; + Q_ASSERT(m_visitor->qmlScopes().size() > 0); + QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount = m_visitor->qmlScopesWithQmlBases(); + QStringList typeCountComponents; + typeCountComponents.reserve(1 + typesWithBaseTypeCount.size()); + // add this document's type counts minus document root + typeCountComponents << QString::number(m_visitor->qmlScopes().size() - 1); + for (const QQmlJSScope::ConstPtr &t : qAsConst(typesWithBaseTypeCount)) { + if (t == type) { // t is this document's root + typeCountComponents << t->baseTypeName() + u"::" + current.typeCount->name; + } else { + typeCountComponents << t->internalName() + u"::" + current.typeCount->name; + } + } + current.typeCount->defaultValue = typeCountComponents.join(u" + "_qs); } - // add special member functions - current.basicCtor.access = QQmlJSMetaMethod::Protected; - current.init.access = QQmlJSMetaMethod::Protected; - current.finalize.access = QQmlJSMetaMethod::Protected; - current.fullCtor.access = QQmlJSMetaMethod::Public; - if (!documentRoot) { - // make document root a friend to allow it to access init and finalize - auto root = m_visitor->result(); - current.otherCode << u"friend class %1;"_qs.arg(root->internalName()); - } current.mocCode = { u"Q_OBJECT"_qs, // Note: isAnonymous holds for non-root types in the document as well isAnonymous ? u"QML_ANONYMOUS"_qs : u"QML_ELEMENT"_qs, }; + // add special member functions + current.basicCtor.access = QQmlJSMetaMethod::Protected; + current.init.access = QQmlJSMetaMethod::Protected; + current.finalize.access = QQmlJSMetaMethod::Protected; + current.fullCtor.access = QQmlJSMetaMethod::Public; + current.basicCtor.name = current.cppType; current.fullCtor.name = current.cppType; current.init.name = u"qmltc_init"_qs; @@ -118,18 +135,20 @@ void QmltcCompiler::compileType(QmltcType ¤t, const QQmlJSScope::ConstPtr current.finalize.name = u"qmltc_finalize"_qs; current.finalize.returnType = u"void"_qs; + QmltcVariable creator(u"QQmltcObjectCreationHelper*"_qs, u"creator"_qs); QmltcVariable engine(u"QQmlEngine*"_qs, u"engine"_qs); QmltcVariable parent(u"QObject*"_qs, u"parent"_qs, u"nullptr"_qs); current.basicCtor.parameterList = { parent }; - current.fullCtor.parameterList = { engine, parent }; QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_qs, u"parentContext"_qs); QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs); if (documentRoot) { - current.init.parameterList = { engine, ctxtdata, finalizeFlag }; - current.finalize.parameterList = { engine, finalizeFlag }; + current.fullCtor.parameterList = { engine, parent }; + current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag }; + current.finalize.parameterList = { creator, engine, finalizeFlag }; } else { - current.init.parameterList = { engine, ctxtdata }; - current.finalize.parameterList = { engine }; + current.fullCtor.parameterList = { creator, engine, parent }; + current.init.parameterList = { creator, engine, ctxtdata }; + current.finalize.parameterList = { creator, engine }; } current.fullCtor.initializerList = { current.basicCtor.name + u"(" + parent.name + u")" }; @@ -145,12 +164,30 @@ void QmltcCompiler::compileType(QmltcType ¤t, const QQmlJSScope::ConstPtr // compilation stub: current.fullCtor.body << u"Q_UNUSED(engine);"_qs; + current.init.body << u"Q_UNUSED(creator);"_qs; current.init.body << u"Q_UNUSED(engine);"_qs; current.init.body << u"Q_UNUSED(parentContext);"_qs; current.finalize.body << u"Q_UNUSED(engine);"_qs; + current.finalize.body << u"Q_UNUSED(creator);"_qs; if (documentRoot) { + current.fullCtor.body << u"// document root:"_qs; + // if it's document root, we want to create our QQmltcObjectCreationBase + // that would store all the created objects + current.fullCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_qs.arg( + type->internalName()); + current.fullCtor.body << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs; + // now call init + current.fullCtor.body << current.init.name + + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* " + u"finalize */ true);"; + current.init.body << u"Q_UNUSED(canFinalize);"_qs; current.finalize.body << u"Q_UNUSED(canFinalize);"_qs; + } else { + current.fullCtor.body << u"// not document root:"_qs; + // just call init, we don't do any setup here otherwise + current.fullCtor.body << current.init.name + + u"(creator, engine, QQmlData::get(parent)->outerContext);"; } current.init.body << u"return nullptr;"_qs; } diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h index dd40ac5ea5..e81caf14ed 100644 --- a/tools/qmltc/qmltcoutputir.h +++ b/tools/qmltc/qmltcoutputir.h @@ -116,12 +116,8 @@ struct QmltcType // member variables: properties and just variables QList<QmltcVariable> variables; - // we want to use certain private/protected functions by document root, so - // record it to make it a friend - std::optional<QString> documentRootType; - // QML document root specific: - std::optional<int> typeCount = 0; // the number of QML types defined in a document + std::optional<QmltcVariable> typeCount; // the number of QML types defined in a document }; // Represents whole QML program, compiled to C++ |