aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2021-10-22 12:02:37 +0200
committerAndrei Golubev <andrei.golubev@qt.io>2021-11-15 12:00:55 +0100
commit0990b892cab819f210120f8a9f9ca48522bde412 (patch)
tree091eb6fd604649f996139986a2fb58d874bfa143 /tools/qmltc
parent539a08d1f4e4a672c33ccb21e4db5770491ebadb (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.cpp20
-rw-r--r--tools/qmltc/qmltccompiler.cpp73
-rw-r--r--tools/qmltc/qmltcoutputir.h6
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 &current, 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 &current, 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 &current, 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++