aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp2
-rw-r--r--src/qml/qmltc/qqmltcobjectcreationhelper_p.h15
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp5
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h2
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h4
-rw-r--r--tools/qmltc/prototype/codegenerator.cpp557
-rw-r--r--tools/qmltc/prototype/codegenerator.h32
-rw-r--r--tools/qmltc/qmltccodewriter.cpp35
-rw-r--r--tools/qmltc/qmltccompiler.cpp227
-rw-r--r--tools/qmltc/qmltccompiler.h5
-rw-r--r--tools/qmltc/qmltccompilerpieces.h279
-rw-r--r--tools/qmltc/qmltcoutputir.h7
-rw-r--r--tools/qmltc/qmltcvisitor.cpp224
-rw-r--r--tools/qmltc/qmltcvisitor.h52
14 files changed, 803 insertions, 643 deletions
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
index 122084f1d8..0e125a1926 100644
--- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
@@ -69,7 +69,7 @@ class HelloWorld : public QObject
Q_PROPERTY(QString hello WRITE setHello READ hello BINDABLE bindableHello)
public:
- HelloWorld(QQmlEngine * engine, QObject * parent = nullptr);
+ HelloWorld(QQmlEngine* engine, QObject* parent = nullptr);
Q_SIGNALS:
void created();
diff --git a/src/qml/qmltc/qqmltcobjectcreationhelper_p.h b/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
index e34bb5506a..6aa37b1e53 100644
--- a/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
+++ b/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
@@ -70,17 +70,15 @@ class QQmltcObjectCreationHelper
QObject **m_data = nullptr; // QObject* array
const qsizetype m_size = 0; // size of m_data array, exists for bounds checking
const qsizetype m_offset = 0; // global offset into m_data array
- const qsizetype m_nonRoot = 1; // addresses the "+ 1" in QQmltcObjectCreationBase::m_objects
- qsizetype offset() const { return m_offset + m_nonRoot; }
+ qsizetype offset() const { return m_offset; }
public:
/*!
Constructs initial "view" from basic data. Supposed to only be called
once from QQmltcObjectCreationBase.
*/
- QQmltcObjectCreationHelper(QObject **data, qsizetype size)
- : m_data(data), m_size(size), m_nonRoot(0 /* root object */)
+ QQmltcObjectCreationHelper(QObject **data, qsizetype size) : m_data(data), m_size(size)
{
Q_UNUSED(m_size);
}
@@ -92,7 +90,6 @@ public:
QQmltcObjectCreationHelper(const QQmltcObjectCreationHelper *base, qsizetype localOffset)
: m_data(base->m_data), m_size(base->m_size), m_offset(base->m_offset + localOffset)
{
- Q_ASSERT(m_nonRoot == 1); // sanity check - a sub-creator is for non-root object
}
template<typename T>
@@ -113,6 +110,12 @@ public:
Q_ASSERT(m_data[i + offset()] == nullptr); // prevent accidental resets
m_data[i + offset()] = object;
}
+
+ template<typename T>
+ static constexpr uint typeCount() noexcept
+ {
+ return T::q_qmltc_typeCount();
+ }
};
/*!
@@ -125,7 +128,7 @@ template<typename QmltcGeneratedType>
class QQmltcObjectCreationBase
{
// Note: +1 for the document root itself
- std::array<QObject *, QmltcGeneratedType::q_qmltc_typeCount + 1> m_objects = {};
+ std::array<QObject *, QmltcGeneratedType::q_qmltc_typeCount() + 1> m_objects = {};
public:
QQmltcObjectCreationHelper view()
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 04f34d83a2..deb6b22331 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -112,8 +112,6 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger
}
for (const auto &jsGlobVar : jsGlobVars)
m_currentScope->insertJSIdentifier(jsGlobVar, globalJavaScript);
-
- m_runtimeIdCounters.push(0); // global (this document's) runtime id counter
}
QQmlJSImportVisitor::~QQmlJSImportVisitor() = default;
@@ -1255,13 +1253,11 @@ bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
m_nextIsInlineComponent = true;
m_inlineComponentName = component->name;
- m_runtimeIdCounters.push(0); // add new id counter, since counters are component-local
return true;
}
void QQmlJSImportVisitor::endVisit(UiInlineComponent *)
{
- m_runtimeIdCounters.pop();
m_inlineComponentName = QStringView();
Q_ASSERT(!m_nextIsInlineComponent);
}
@@ -1576,7 +1572,6 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
}
if (!name.isEmpty())
m_scopesById.insert(name, m_currentScope);
- m_currentScope->setRuntimeId(m_runtimeIdCounters.top()++);
}
bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index f89aa03dc5..69714ced36 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -290,8 +290,6 @@ protected:
QSet<QQmlJSScope::ConstPtr> m_literalScopesToCheck;
QQmlJS::SourceLocation m_pendingSignalHandler;
- QStack<int> m_runtimeIdCounters;
-
private:
void importBaseModules();
void resolveAliasesAndIds();
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 4e692a0d01..da670f0c71 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -430,9 +430,6 @@ public:
AccessSemantics accessSemantics() const { return m_semantics; }
bool isReferenceType() const { return m_semantics == QQmlJSScope::AccessSemantics::Reference; }
- void setRuntimeId(int id) { m_runtimeId = id; }
- int runtimeId() const { return m_runtimeId; }
-
bool isIdInCurrentQmlScopes(const QString &id) const;
bool isIdInCurrentJSScopes(const QString &id) const;
bool isIdInjectedFromSignal(const QString &id) const;
@@ -617,7 +614,6 @@ private:
AccessSemantics m_semantics = AccessSemantics::Reference;
QQmlJS::SourceLocation m_sourceLocation;
- int m_runtimeId = -1; // an index counterpart of "foobar" in `id: foobar`
};
Q_DECLARE_TYPEINFO(QQmlJSScope::QmlIRCompatibilityBindingData, Q_RELOCATABLE_TYPE);
diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp
index 599bf9fba1..6be93d7756 100644
--- a/tools/qmltc/prototype/codegenerator.cpp
+++ b/tools/qmltc/prototype/codegenerator.cpp
@@ -157,6 +157,7 @@ QHash<uint, qsizetype> QmlIrBindingCompare::orderTable = {
{ QmlIR::Binding::Type_Number, 0 },
{ QmlIR::Binding::Type_String, 0 },
{ QmlIR::Binding::Type_Object, 0 },
+ { QmlIR::Binding::Type_Null, 0 },
// translations are also "equal" between themselves, but come after
// assignments
{ QmlIR::Binding::Type_Translation, 1 },
@@ -167,12 +168,12 @@ QHash<uint, qsizetype> QmlIrBindingCompare::orderTable = {
{ QmlIR::Binding::Type_GroupProperty, 2 },
// JS bindings come last because they can use values from other categories
{ QmlIR::Binding::Type_Script, 3 },
- // { QmlIR::Binding::Type_Null, 100 }, // TODO: what is this used for?
};
-static QList<typename QmlIR::PoolList<QmlIR::Binding>::Iterator>
-toOrderedSequence(typename QmlIR::PoolList<QmlIR::Binding>::Iterator first,
- typename QmlIR::PoolList<QmlIR::Binding>::Iterator last, qsizetype n)
+QList<typename QmlIR::PoolList<QmlIR::Binding>::Iterator>
+CodeGenerator::toOrderedSequence(typename QmlIR::PoolList<QmlIR::Binding>::Iterator first,
+ typename QmlIR::PoolList<QmlIR::Binding>::Iterator last,
+ qsizetype n)
{
// bindings actually have to sorted so that e.g. value assignments come
// before script bindings. this is important for the code generator as it
@@ -184,7 +185,8 @@ toOrderedSequence(typename QmlIR::PoolList<QmlIR::Binding>::Iterator first,
sorted << it;
// NB: use stable sort for bindings, because this matters: relative order of
// bindings must be preserved - this might affect UI
- std::stable_sort(sorted.begin(), sorted.end(), QmlIrBindingCompare {});
+ std::stable_sort(sorted.begin(), sorted.end(),
+ QmlIrBindingCompare {}); // TODO: use stable_partition instead
return sorted;
}
@@ -238,8 +240,14 @@ static const QmltcVariable compilationUnitVariable { u"QV4::ExecutableCompilatio
Q_LOGGING_CATEGORY(lcCodeGen, "qml.compiler.CodeGenerator", QtWarningMsg);
CodeGenerator::CodeGenerator(const QString &url, QQmlJSLogger *logger, QmlIR::Document *doc,
- const QmltcTypeResolver *localResolver, const QmltcCompilerInfo *info)
- : m_url(url), m_logger(logger), m_doc(doc), m_localTypeResolver(localResolver), m_info(info)
+ const QmltcTypeResolver *localResolver, const QmltcVisitor *visitor,
+ const QmltcCompilerInfo *info)
+ : m_url(url),
+ m_logger(logger),
+ m_doc(doc),
+ m_localTypeResolver(localResolver),
+ m_visitor(visitor),
+ m_info(info)
{
Q_ASSERT(m_info);
Q_ASSERT(!m_info->outputHFile.isEmpty());
@@ -350,405 +358,10 @@ QString buildCallSpecialMethodValue(bool documentRoot, const QString &outerFlagN
}
}
-void CodeGenerator::compileObject(
- QmltcType &compiled, const CodeGenObject &object,
- std::function<void(QmltcType &, const CodeGenObject &)> compileElements)
+static QString generate_callCompilationUnit(const QString &urlMethodName)
{
- if (object.type->isSingleton()) {
- recordError(object.type->sourceLocation(), u"Singleton types are not supported"_qs);
- return;
- }
-
- compiled.cppType = object.type->internalName();
- const QString baseClass = object.type->baseType()->internalName();
-
- const bool baseTypeIsCompiledQml = m_qmlCompiledBaseTypes.contains(object.type->baseTypeName());
- const qsizetype objectIndex = m_typeToObjectIndex[object.type];
- const bool documentRoot = objectIndex == 0;
- const bool hasParserStatusInterface = object.type->hasInterface(u"QQmlParserStatus"_qs);
- const bool hasFinalizerHookInterface = object.type->hasInterface(u"QQmlFinalizerHook"_qs);
-
- compiled.baseClasses = { baseClass };
-
- // add ctors code
- compiled.baselineCtor.access = QQmlJSMetaMethod::Protected;
- if (documentRoot) {
- compiled.externalCtor.access = QQmlJSMetaMethod::Public;
- } else {
- compiled.externalCtor.access = QQmlJSMetaMethod::Protected;
- }
- compiled.init.access = QQmlJSMetaMethod::Protected;
- compiled.endInit.access = QQmlJSMetaMethod::Protected;
- compiled.completeComponent.access = QQmlJSMetaMethod::Protected;
- compiled.finalizeComponent.access = QQmlJSMetaMethod::Protected;
- compiled.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
-
- compiled.baselineCtor.name = compiled.cppType;
- compiled.externalCtor.name = compiled.cppType;
- compiled.init.name = u"QML_init"_qs;
- compiled.init.returnType = u"QQmlRefPointer<QQmlContextData>"_qs;
- compiled.endInit.name = u"QML_endInit"_qs;
- compiled.endInit.returnType = u"void"_qs;
- compiled.completeComponent.name = u"QML_completeComponent"_qs;
- compiled.completeComponent.returnType = u"void"_qs;
- compiled.finalizeComponent.name = u"QML_finalizeComponent"_qs;
- compiled.finalizeComponent.returnType = u"void"_qs;
- compiled.handleOnCompleted.name = u"QML_handleOnCompleted"_qs;
- compiled.handleOnCompleted.returnType = u"void"_qs;
-
- QmltcVariable engine(u"QQmlEngine *"_qs, u"engine"_qs, QString());
- QmltcVariable parent(u"QObject *"_qs, u"parent"_qs, u"nullptr"_qs);
- compiled.baselineCtor.parameterList = { parent };
- compiled.externalCtor.parameterList = { engine, parent };
- QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData> &"_qs, u"parentContext"_qs,
- QString());
- QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs, QString());
- QmltcVariable callSpecialMethodFlag(u"bool"_qs, u"callSpecialMethodNow"_qs, QString());
- if (documentRoot) {
- compiled.init.parameterList = { engine, ctxtdata, finalizeFlag, callSpecialMethodFlag };
- compiled.endInit.parameterList = { engine, finalizeFlag };
- compiled.completeComponent.parameterList = { callSpecialMethodFlag };
- compiled.finalizeComponent.parameterList = { callSpecialMethodFlag };
- } else {
- compiled.init.parameterList = { engine, ctxtdata };
- compiled.endInit.parameterList = { engine, compilationUnitVariable };
- }
-
- if (!documentRoot) {
- // make document root a friend to allow protected member function access
- Q_ASSERT(m_objects[0].type);
- compiled.otherCode << u"friend class %1;"_qs.arg(m_objects[0].type->internalName());
- // additionally, befriend the immediate parent of this type
- if (auto parent = m_immediateParents.value(object.type);
- parent && parent != m_objects[0].type) {
- compiled.otherCode << u"friend class %1;"_qs.arg(parent->internalName());
- }
- }
-
- if (baseTypeIsCompiledQml) {
- // call baseline ctor of the QML-originated base class. it also takes
- // care of QObject::setParent() call
- compiled.baselineCtor.initializerList = { baseClass + u"(parent)" };
- } else {
- // default call to ctor is enough, but QQml_setParent_noEvent() - is
- // needed (note, this is a faster version of QObject::setParent())
- compiled.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
- }
-
- compiled.externalCtor.initializerList = { compiled.baselineCtor.name + u"(parent)" };
- if (documentRoot) {
- compiled.externalCtor.body << u"// document root:"_qs;
- compiled.endInit.body << u"auto " + compilationUnitVariable.name + u" = "
- + u"QQmlEnginePrivate::get(engine)->compilationUnitFromUrl("
- + m_urlMethodName + u"());";
-
- // call init method of the document root
- compiled.externalCtor.body << compiled.init.name
- + u"(engine, QQmlContextData::get(engine->rootContext()), /* finalize */ "
- u"true, /* call special method */ true);";
- } else {
- compiled.externalCtor.body << u"// not document root:"_qs;
- compiled.externalCtor.body
- << compiled.init.name + u"(engine, QQmlData::get(parent)->outerContext);";
- }
-
- compiled.init.body << u"Q_UNUSED(engine);"_qs;
- if (documentRoot) {
- compiled.init.body << u"Q_UNUSED(" + callSpecialMethodFlag.name + u")";
- compiled.completeComponent.body << u"Q_UNUSED(" + callSpecialMethodFlag.name + u")";
- compiled.finalizeComponent.body << u"Q_UNUSED(" + callSpecialMethodFlag.name + u")";
- }
-
- // compiled.init.body << u"Q_UNUSED(" + finalizeFlag.name + u");";
- compiled.init.body << u"auto context = parentContext;"_qs;
- // TODO: context hierarchy is way-over-the-top complicated already
-
- // -1. if the parent scope of this type has base type as compiled qml and
- // this parent scope is not a root object, we have to go one level up in the
- // context. in a nutshell:
- // * parentScope->outerContext == parentContext of this type
- // * parentScope->outerContext != context of this document
- // * parentScope->outerContext is a child of context of this document
- // > to ensure correct context, we must use context->parent() instead of
- // > parentContext
- if (QQmlJSScope::ConstPtr parent = object.type->parentScope(); parent
- && m_qmlCompiledBaseTypes.contains(parent->baseTypeName())
- && m_typeToObjectIndex[parent] != 0) {
- compiled.init.body << u"// NB: context->parent() is the context of the root "_qs;
- compiled.init.body << u"context = context->parent();"_qs;
- }
-
- // 0. call parent's init if necessary
- if (baseTypeIsCompiledQml) {
- compiled.init.body << u"// 0. call parent's init"_qs;
- QString lhs;
- if (documentRoot)
- lhs = u"context = "_qs;
- const QString callParserStatusSpecialMethod = buildCallSpecialMethodValue(
- documentRoot, callSpecialMethodFlag.name, hasParserStatusInterface);
- compiled.init.body << lhs + baseClass + u"::" + compiled.init.name
- + u"(engine, context, /* finalize */ false, /* call special method */ "
- + callParserStatusSpecialMethod + u");";
-
- compiled.completeComponent.body << u"// call parent's completeComponent"_qs;
- compiled.completeComponent.body << baseClass + u"::" + compiled.completeComponent.name
- + u"(" + callParserStatusSpecialMethod + u");";
-
- const QString callFinalizerHookSpecialMethod = buildCallSpecialMethodValue(
- documentRoot, callSpecialMethodFlag.name, hasFinalizerHookInterface);
- compiled.finalizeComponent.body << u"// call parent's finalizeComponent"_qs;
- compiled.finalizeComponent.body << baseClass + u"::" + compiled.finalizeComponent.name
- + u"(" + callFinalizerHookSpecialMethod + u");";
-
- compiled.handleOnCompleted.body << u"// call parent's Component.onCompleted handler"_qs;
- compiled.handleOnCompleted.body
- << baseClass + u"::" + compiled.handleOnCompleted.name + u"();";
- }
- // 1. create new context through QQmlCppContextRegistrator
- if (documentRoot) {
- Q_ASSERT(objectIndex == 0);
- compiled.init.body << u"// 1. create context for this type (root)"_qs;
- compiled.init.body
- << QStringLiteral(
- "context = %1->createInternalContext(%1->compilationUnitFromUrl(%2()), "
- "context, 0, true);")
- .arg(u"QQmlEnginePrivate::get(engine)"_qs, m_urlMethodName);
- } else {
- // non-root objects adopt parent context and use that one instead of
- // creating own context
- compiled.init.body << u"// 1. use current as context of this type (non-root)"_qs;
- compiled.init.body << u"// context = context;"_qs;
- }
-
- // TODO: optimize step 2: do we need context = parentContext? simplify
- // QQmlCppContextRegistrator::set also
-
- // 2.
- if (baseTypeIsCompiledQml && !documentRoot) {
- } else { // !baseTypeIsCompiledQml || documentRoot
- // set up current context
- compiled.init.body << u"// 2. normal flow, set context for this object"_qs;
- const QString enumValue = documentRoot ? u"DocumentRoot"_qs : u"OrdinaryObject"_qs;
- compiled.init.body << QStringLiteral(
- "%1->setInternalContext(this, context, QQmlContextData::%2);")
- .arg(u"QQmlEnginePrivate::get(engine)"_qs, enumValue);
- if (documentRoot)
- compiled.init.body << u"context->setContextObject(this);"_qs;
- }
- // 3. set id if it's present in the QML document
- if (!m_doc->stringAt(object.irObject->idNameIndex).isEmpty()) {
- compiled.init.body << u"// 3. set id since it exists"_qs;
- QmltcCodeGenerator::generate_setIdValue(&compiled.init.body, u"context"_qs,
- object.irObject->id, u"this"_qs,
- m_doc->stringAt(object.irObject->idNameIndex));
- }
-
- // TODO: we might want to optimize storage space when there are no object
- // bindings, but this requires deep checking (e.g. basically go over all
- // bindings and all bindings of attached/grouped properties)
- compiled.init.body << u"// create objects for object bindings in advance:"_qs;
- // TODO: support private and protected variables
- compiled.variables.emplaceBack(childrenOffsetVariable);
- compiled.init.body << childrenOffsetVariable.name + u" = QObject::children().size();";
-
- // magic step: if the type has a QQmlParserStatus interface, we should call
- // it's method here
- if (hasParserStatusInterface) {
- const QString indent = documentRoot ? u" "_qs : QString();
-
- compiled.init.body << u"// this type has QQmlParserStatus interface:"_qs;
- compiled.init.body << u"Q_ASSERT(dynamic_cast<QQmlParserStatus *>(this) != nullptr);"_qs;
- if (documentRoot)
- compiled.init.body << u"if (" + callSpecialMethodFlag.name + u")";
- compiled.init.body << indent + u"this->classBegin();";
-
- compiled.completeComponent.lastLines << u"// this type has QQmlParserStatus interface:"_qs;
- compiled.completeComponent.lastLines
- << u"Q_ASSERT(dynamic_cast<QQmlParserStatus *>(this) != nullptr);"_qs;
- if (documentRoot)
- compiled.completeComponent.lastLines << u"if (" + callSpecialMethodFlag.name + u")";
- compiled.completeComponent.lastLines << indent + u"this->componentComplete();";
- }
-
- // magic step: if the type has a QQmlFinalizerHook interface, we should call
- // it's method here
- if (hasFinalizerHookInterface) {
- QString indent;
- compiled.finalizeComponent.lastLines << u"// this type has QQmlFinalizerHook interface:"_qs;
- compiled.finalizeComponent.lastLines
- << u"Q_ASSERT(dynamic_cast<QQmlFinalizerHook *>(this) != nullptr);"_qs;
- if (documentRoot) {
- compiled.finalizeComponent.lastLines << u"if (" + callSpecialMethodFlag.name + u")";
- indent = u" "_qs;
- }
- compiled.finalizeComponent.lastLines << indent + u"this->componentFinalized();"_qs;
- }
-
- // NB: step 4 - and everything below that - is done at the very end of QML
- // init and so we use lastLines. we need to make sure that objects from
- // object bindings are created (and initialized) before we start the
- // finalization: we need fully-set context at the beginning of QML finalize.
-
- // 4. finalize if necessary
- if (documentRoot) {
- compiled.init.lastLines << u"// 4. document root, call finalize"_qs;
- compiled.init.lastLines << u"if (" + finalizeFlag.name + u") {";
- compiled.init.lastLines << u" " + compiled.endInit.name
- + u"(engine, /* finalize */ true);";
- compiled.init.lastLines << u"}"_qs;
- }
- // return
- compiled.init.lastLines << u"return context;"_qs;
-
- // TODO: is property update group needed?
- compiled.endInit.body << u"Q_UNUSED(engine);"_qs;
- compiled.endInit.body << u"Q_UNUSED(" + compilationUnitVariable.name + u")"_qs;
- // compiled.endInit.body << u"Q_UNUSED(" + finalizeFlag.name + u");";
- if (baseTypeIsCompiledQml) {
- compiled.endInit.body << u"{ // call parent's finalize"_qs;
- compiled.endInit.body << baseClass + u"::" + compiled.endInit.name
- + u"(engine, /* finalize */ false);";
- compiled.endInit.body << u"}"_qs;
- }
-
- if (object.irObject->flags & QV4::CompiledData::Object::HasDeferredBindings) {
- compiled.endInit.body << u"{ // defer bindings"_qs;
- compiled.endInit.body << u"auto ddata = QQmlData::get(this);"_qs;
- compiled.endInit.body << u"auto thisContext = ddata->outerContext;"_qs;
- compiled.endInit.body << u"Q_ASSERT(thisContext);"_qs;
- compiled.endInit.body << u"ddata->deferData(" + QString::number(objectIndex) + u", "
- + compilationUnitVariable.name + u", thisContext);";
- compiled.endInit.body << u"}"_qs;
- }
-
- // TODO: decide whether begin/end property update group is needed
- // compiled.endInit.body << u"Qt::beginPropertyUpdateGroup(); // defer binding evaluation"_qs;
-
- // add basic MOC stuff
- compiled.mocCode = {
- u"Q_OBJECT"_qs,
- (m_isAnonymous ? u"QML_ANONYMOUS"_qs : u"QML_ELEMENT"_qs),
- };
-
- compileElements(compiled, object);
-
- // add finalization steps only to document root
- if (documentRoot) {
- compiled.endInit.body << u"if (" + finalizeFlag.name + u") {";
-
- // at this point, all bindings must've been finished, thus, we need:
- // 1. componentComplete()
- // 2. finalize callbacks / componentFinalized()
- // 3. Component.onCompleted()
-
- // 1.
- compiled.endInit.body << u" this->" + compiled.completeComponent.name
- + u"(/* complete component */ true);";
-
- // 2
- compiled.endInit.body << u" this->" + compiled.finalizeComponent.name
- + u"(/* finalize component */ true);";
-
- // 3.
- compiled.endInit.body << u" this->" + compiled.handleOnCompleted.name + u"();";
-
- compiled.endInit.body << u"}"_qs;
- }
-
- // compiled.endInit.body << u"Qt::endPropertyUpdateGroup();"_qs;
-}
-
-void CodeGenerator::compileObjectElements(QmltcType &compiled, const CodeGenObject &object)
-{
- // compile enums
- const auto enums = object.type->ownEnumerations();
- compiled.enums.reserve(enums.size());
- for (auto it = enums.cbegin(); it != enums.cend(); ++it)
- compileEnum(compiled, it.value());
-
- // compile properties in order
- auto properties = object.type->ownProperties().values();
- compiled.variables.reserve(properties.size());
- std::sort(properties.begin(), properties.end(),
- [](const QQmlJSMetaProperty &x, const QQmlJSMetaProperty &y) {
- return x.index() < y.index();
- });
- for (const QQmlJSMetaProperty &p : properties) {
- if (p.index() == -1) {
- recordError(object.type->sourceLocation(),
- u"Property '" + p.propertyName()
- + u"' has incomplete information (internal error)");
- continue;
- }
- if (p.isAlias()) {
- compileAlias(compiled, p, object.type);
- } else { // normal property
- compileProperty(compiled, p, object.type);
- }
- }
-
- // compile methods
- QHash<QString, const QmlIR::Function *> irFunctionsByName;
- std::for_each(object.irObject->functionsBegin(), object.irObject->functionsEnd(),
- [&](const QmlIR::Function &function) {
- irFunctionsByName.insert(m_doc->stringAt(function.nameIndex),
- std::addressof(function));
- });
- const auto methods = object.type->ownMethods();
- compiled.functions.reserve(methods.size());
- for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
- const QmlIR::Function *irFunction = irFunctionsByName.value(it.key(), nullptr);
- compileMethod(compiled, it.value(), irFunction, object);
- }
-
- // NB: just clearing is safe since we do not call this function recursively
- m_localChildrenToEndInit.clear();
- m_localChildrenToFinalize.clear();
-
- // compile bindings
- const auto sortedBindings =
- toOrderedSequence(object.irObject->bindingsBegin(), object.irObject->bindingsEnd(),
- object.irObject->bindingCount());
-
- // for (auto it : sortedBindings)
- // compileBinding(compiled, *it, object, u"this"_qs);
-
- // NB: can't use lower_bound since it only accepts a value, not a unary
- // predicate
- auto scriptBindingsBegin =
- std::find_if(sortedBindings.cbegin(), sortedBindings.cend(),
- [](auto it) { return it->type == QmlIR::Binding::Type_Script; });
- auto it = sortedBindings.cbegin();
- for (; it != scriptBindingsBegin; ++it)
- compileBinding(compiled, **it, object, { object.type, u"this"_qs, u""_qs, false });
-
- // NB: finalize children before creating/setting script bindings for `this`
- for (qsizetype i = 0; i < m_localChildrenToEndInit.size(); ++i) {
- compiled.endInit.body << m_localChildrenToEndInit.at(i) + u"->" + compiled.endInit.name
- + u"(engine, " + compilationUnitVariable.name + u");";
- }
-
- const auto buildChildAtString = [](const QQmlJSScope::ConstPtr &type,
- const QString &i) -> QString {
- return u"static_cast<" + type->internalName() + u"* >(QObject::children().at("
- + childrenOffsetVariable.name + u" + " + i + u"))";
- };
- // TODO: there's exceptional redundancy (!) in this code generation part
- for (qsizetype i = 0; i < m_localChildrenToFinalize.size(); ++i) {
- QString index = QString::number(i);
- const QQmlJSScope::ConstPtr &child = m_localChildrenToFinalize.at(i);
- const QString childAt = buildChildAtString(child, index);
- // NB: children are not document roots, so all special methods are argument-less
- compiled.completeComponent.body
- << childAt + u"->" + compiled.completeComponent.name + u"();";
- compiled.finalizeComponent.body
- << childAt + u"->" + compiled.finalizeComponent.name + u"();";
- compiled.handleOnCompleted.body
- << childAt + u"->" + compiled.handleOnCompleted.name + u"();";
- }
-
- for (; it != sortedBindings.cend(); ++it)
- compileBinding(compiled, **it, object, { object.type, u"this"_qs, u""_qs, false });
+ // NB: assume `engine` variable always exists
+ return u"QQmlEnginePrivate::get(engine)->compilationUnitFromUrl(%1())"_qs.arg(urlMethodName);
}
void CodeGenerator::compileQQmlComponentElements(QmltcType &compiled, const CodeGenObject &object)
@@ -785,102 +398,6 @@ void CodeGenerator::compileQQmlComponentElements(QmltcType &compiled, const Code
compiled.init.body << u"}"_qs;
}
-void CodeGenerator::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
-{
- const auto intValues = e.values();
- QStringList values;
- values.reserve(intValues.size());
- std::transform(intValues.cbegin(), intValues.cend(), std::back_inserter(values),
- [](int x) { return QString::number(x); });
-
- // structure: (C++ type name, enum keys, enum values, MOC line)
- current.enums.emplaceBack(e.name(), e.keys(), std::move(values),
- u"Q_ENUM(%1)"_qs.arg(e.name()));
-}
-
-void CodeGenerator::compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
- const QQmlJSScope::ConstPtr &owner)
-{
- Q_ASSERT(!p.isAlias()); // will be handled separately
- Q_ASSERT(p.type());
-
- const QString name = p.propertyName();
- const QString variableName = u"m_" + name;
- const QString underlyingType = getUnderlyingType(p);
- // only check for isList() here as it needs some special arrangements.
- // otherwise, getUnderlyingType() handles the specifics of a type in C++
- if (p.isList()) {
- const QString storageName = variableName + u"_storage";
- current.variables.emplaceBack(u"QList<" + p.type()->internalName() + u" *>", storageName,
- QString());
- current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
- + u"(this, std::addressof(" + storageName
- + u")))");
- }
-
- // along with property, also add relevant moc code, so that we can use the
- // property in Qt/QML contexts
- QStringList mocPieces;
- mocPieces.reserve(10);
- mocPieces << underlyingType << name;
-
- QmltcPropertyData compilationData(p);
-
- // 1. add setter and getter
- // If p.isList(), it's a QQmlListProperty. Then you can write the underlying list through
- // the QQmlListProperty object retrieved with the getter. Setting it would make no sense.
- if (p.isWritable() && !p.isList()) {
- QmltcMethod setter {};
- setter.returnType = u"void"_qs;
- setter.name = compilationData.write;
- // QmltcVariable
- setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
- u""_qs);
- setter.body << variableName + u".setValue(" + name + u"_);";
- setter.body << u"emit " + compilationData.notify + u"();";
- setter.userVisible = true;
- current.functions.emplaceBack(setter);
- mocPieces << u"WRITE"_qs << setter.name;
- }
-
- QmltcMethod getter {};
- getter.returnType = underlyingType;
- getter.name = compilationData.read;
- getter.body << u"return " + variableName + u".value();";
- getter.userVisible = true;
- current.functions.emplaceBack(getter);
- mocPieces << u"READ"_qs << getter.name;
-
- // 2. add bindable
- if (!p.isList()) {
- QmltcMethod bindable {};
- bindable.returnType = u"QBindable<" + underlyingType + u">";
- bindable.name = compilationData.bindable;
- bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
- + u"));";
- bindable.userVisible = true;
- current.functions.emplaceBack(bindable);
- mocPieces << u"BINDABLE"_qs << bindable.name;
- }
-
- // 3. add/check notify (actually, this is already done inside QmltcVisitor)
-
- if (owner->isPropertyRequired(name))
- mocPieces << u"REQUIRED"_qs;
-
- // 4. add moc entry
- // e.g. Q_PROPERTY(QString p READ getP WRITE setP BINDABLE bindableP)
- current.mocCode << u"Q_PROPERTY(" + mocPieces.join(u" "_qs) + u")";
-
- // 5. add extra moc entry if this property is marked default
- if (name == owner->defaultPropertyName())
- current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_qs.arg(name);
-
- // structure: (C++ type name, name, C++ class name, C++ signal name)
- current.properties.emplaceBack(underlyingType, variableName, current.cppType,
- compilationData.notify);
-}
-
void CodeGenerator::compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner)
{
@@ -1256,15 +773,16 @@ void CodeGenerator::compileBinding(QmltcType &current, const QmlIR::Binding &bin
const auto uniqueId = UniqueStringId(current, onAssignmentName);
if (!m_onAssignmentObjectsCreated.contains(uniqueId)) {
m_onAssignmentObjectsCreated.insert(uniqueId);
- current.init.body << u"new " + bindingObject.type->internalName() + u"(engine, "
- + qobjectParent + u");";
+ current.init.body << u"auto %1 = new %2(creator, engine, %3);"_qs.arg(
+ onAssignmentName, bindingObject.type->internalName(), qobjectParent);
+ current.init.body << u"creator->set(%1, %2);"_qs.arg(
+ QString::number(m_visitor->creationIndex(bindingObject.type)),
+ onAssignmentName);
// static_cast is fine, because we (must) know the exact type
- current.endInit.body << u"auto " + onAssignmentName + u" = static_cast<"
- + bindingObject.type->internalName()
- + u" *>(QObject::children().at(" + childrenOffsetVariable.name
- + u" + " + QString::number(m_localChildrenToEndInit.size())
- + u"));";
+ current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_qs.arg(
+ onAssignmentName, bindingObject.type->internalName(),
+ QString::number(m_visitor->creationIndex(bindingObject.type)));
m_localChildrenToEndInit.append(onAssignmentName);
m_localChildrenToFinalize.append(bindingObject.type);
@@ -1324,7 +842,8 @@ void CodeGenerator::compileBinding(QmltcType &current, const QmlIR::Binding &bin
current.endInit.body << QStringLiteral(
"auto %1 = QQmlObjectCreator::createComponent(engine, "
"%2, %3, %4, thisContext);")
- .arg(objectName, compilationUnitVariable.name,
+ .arg(objectName,
+ generate_callCompilationUnit(m_urlMethodName),
QString::number(index), qobjectParent);
current.endInit.body << QStringLiteral("thisContext->installContext(QQmlData::get(%1), "
"QQmlContextData::OrdinaryObject);")
@@ -1345,16 +864,16 @@ void CodeGenerator::compileBinding(QmltcType &current, const QmlIR::Binding &bin
break;
}
- current.init.body << u"new " + bindingObject.type->internalName() + u"(engine, "
- + qobjectParent + u");";
-
const QString objectName = makeGensym(u"o"_qs);
-
- // static_cast is fine, because we (must) know the exact type
- current.endInit.body << u"auto " + objectName + u" = static_cast<"
- + bindingObject.type->internalName() + u" *>(QObject::children().at("
- + childrenOffsetVariable.name + u" + "
- + QString::number(m_localChildrenToEndInit.size()) + u"));";
+ current.init.body << u"auto %1 = new %2(creator, engine, %3);"_qs.arg(
+ objectName, bindingObject.type->internalName(), qobjectParent);
+ current.init.body << u"creator->set(%1, %2);"_qs.arg(
+ QString::number(m_visitor->creationIndex(bindingObject.type)), objectName);
+
+ // refetch the same object during endInit to set the bindings
+ current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_qs.arg(
+ objectName, bindingObject.type->internalName(),
+ QString::number(m_visitor->creationIndex(bindingObject.type)));
setObjectBinding(objectName);
m_localChildrenToEndInit.append(objectName);
@@ -1692,7 +1211,7 @@ void CodeGenerator::compileScriptBinding(QmltcType &current, const QmlIR::Bindin
}
QmltcCodeGenerator::generate_createBindingOnProperty(
- &current.endInit.body, compilationUnitVariable.name,
+ &current.endInit.body, generate_callCompilationUnit(m_urlMethodName),
u"this"_qs, // NB: always using enclosing object as a scope for the binding
relativeToAbsoluteRuntimeIndex(object.irObject, binding.value.compiledScriptIndex),
bindingTarget, // binding target
diff --git a/tools/qmltc/prototype/codegenerator.h b/tools/qmltc/prototype/codegenerator.h
index a50bf46116..ae7df452a8 100644
--- a/tools/qmltc/prototype/codegenerator.h
+++ b/tools/qmltc/prototype/codegenerator.h
@@ -51,7 +51,8 @@ class CodeGenerator
{
public:
CodeGenerator(const QString &url, QQmlJSLogger *logger, QmlIR::Document *doc,
- const QmltcTypeResolver *localResolver, const QmltcCompilerInfo *info);
+ const QmltcTypeResolver *localResolver, const QmltcVisitor *visitor,
+ const QmltcCompilerInfo *info);
// TODO: this should really be just QQmlJSScope::ConstPtr (and maybe C++
// class name), but bindings are currently not represented in QQmlJSScope,
@@ -65,11 +66,30 @@ public:
const QList<CodeGenObject> &objects() const { return m_objects; }
bool ignoreObject(const CodeGenObject &object) const;
+ qsizetype codegenObjectIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(m_typeToObjectIndex.contains(type));
+ return m_typeToObjectIndex[type];
+ }
+
+ const CodeGenObject &objectFromType(const QQmlJSScope::ConstPtr &type) const
+ {
+ return m_objects[codegenObjectIndex(type)];
+ }
+
+ QString stringAt(int index) const { return m_doc->stringAt(index); }
+
+ // QmlIR::Binding-specific sort function
+ static QList<typename QmlIR::PoolList<QmlIR::Binding>::Iterator>
+ toOrderedSequence(typename QmlIR::PoolList<QmlIR::Binding>::Iterator first,
+ typename QmlIR::PoolList<QmlIR::Binding>::Iterator last, qsizetype n);
+
private:
QString m_url; // document url
QQmlJSLogger *m_logger = nullptr;
QmlIR::Document *m_doc = nullptr;
const QmltcTypeResolver *m_localTypeResolver = nullptr;
+ const QmltcVisitor *m_visitor = nullptr;
const QmltcCompilerInfo *m_info = nullptr;
@@ -145,16 +165,8 @@ private:
bool m_isAnonymous = false; // crutch to distinguish QML_ELEMENT from QML_ANONYMOUS
public:
- // code compilation functions that produce "compiled" entities
- void compileObject(QmltcType &current, const CodeGenObject &object,
- std::function<void(QmltcType &, const CodeGenObject &)> compileElements);
- void compileObjectElements(QmltcType &current, const CodeGenObject &object);
void compileQQmlComponentElements(QmltcType &current, const CodeGenObject &object);
-private:
- void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
- void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
- const QQmlJSScope::ConstPtr &owner);
void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner);
void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m, const QmlIR::Function *f,
@@ -173,6 +185,8 @@ private:
};
void compileBinding(QmltcType &current, const QmlIR::Binding &binding,
const CodeGenObject &object, const AccessorData &accessor);
+
+private:
// special case (for simplicity)
void compileScriptBinding(QmltcType &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName, const CodeGenObject &object,
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index f4aaf693d7..a70ae029ba 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -145,6 +145,7 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
code.rawAppendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
code.rawAppendToHeader(u"#include <private/qqmlengine_p.h>"); // executeRuntimeFunction(), etc.
+ code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>"); // QmltcSupportLib
code.rawAppendToHeader(u"#include <QtQml/qqmllist.h>"); // QQmlListProperty
@@ -225,6 +226,22 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
// write all the types and their content
for (const QmltcType &type : qAsConst(program.compiledTypes))
write(code, type);
+
+ // add typeCount definitions. after all types have been written down (so
+ // they are now complete types as per C++). practically, this only concerns
+ // document root type
+ for (const QmltcType &type : qAsConst(program.compiledTypes)) {
+ if (!type.typeCount)
+ continue;
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"constexpr %1 %2::%3()"_qs.arg(type.typeCount->returnType,
+ type.cppType, type.typeCount->name));
+ code.rawAppendToHeader(u"{");
+ for (const QString &line : qAsConst(type.typeCount->body))
+ code.rawAppendToHeader(line, 1);
+ code.rawAppendToHeader(u"}");
+ }
+
writeGlobalFooter(code, program.url, program.outNamespace);
writeToFile(program.hPath, code.code().header.toUtf8());
@@ -272,9 +289,6 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
for (const QString &mocLine : qAsConst(type.mocCode))
code.rawAppendToHeader(mocLine, 1);
- for (const QString &otherLine : qAsConst(type.otherCode))
- code.rawAppendToHeader(otherLine, 1);
-
QmltcOutputWrapper::MemberNameScope typeScope(&code, type.cppType);
Q_UNUSED(typeScope);
{
@@ -331,11 +345,10 @@ 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.beginClass);
QmltcCodeWriter::write(code, type.completeComponent);
QmltcCodeWriter::write(code, type.finalizeComponent);
QmltcCodeWriter::write(code, type.handleOnCompleted);
-
- // code.rawAppendToHeader(u"public:", -1);
}
// children
@@ -356,14 +369,16 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
write(code, variable);
}
+ code.rawAppendToHeader(u"private:", -1);
+ for (const QString &otherLine : qAsConst(type.otherCode))
+ code.rawAppendToHeader(otherLine, 1);
+
if (type.typeCount) {
- // we know that typeCount variable is very special
+ // add typeCount declaration, definition is added later
code.rawAppendToHeader(u""); // blank line
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),
+ code.rawAppendToHeader(u"constexpr static %1 %2();"_qs.arg(type.typeCount->returnType,
+ type.typeCount->name),
1);
}
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index ad8e231a7c..0a55fd3bbf 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -45,6 +45,7 @@ Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_qs;
const QString QmltcCodeGenerator::urlMethodName = u"q_qmltc_docUrl"_qs;
+const QString QmltcCodeGenerator::typeCountName = u"q_qmltc_typeCount"_qs;
QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
QQmlJSLogger *logger)
@@ -66,7 +67,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info, QmlIR::Document *doc)
Q_ASSERT(!m_info.resourcePath.isEmpty());
m_prototypeCodegen =
- std::make_unique<CodeGenerator>(m_url, m_logger, doc, m_typeResolver, &info);
+ std::make_unique<CodeGenerator>(m_url, m_logger, doc, m_typeResolver, m_visitor, &info);
QSet<QString> cppIncludesFromPrototype;
m_prototypeCodegen->prepare(&cppIncludesFromPrototype);
@@ -78,8 +79,10 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info, QmlIR::Document *doc)
return base && base->internalName() == u"QQmlComponent"_qs;
};
- auto qmlTypes = m_visitor->qmlTypes();
- const QSet<QQmlJSScope::ConstPtr> types(qmlTypes.begin(), qmlTypes.end());
+ // Note: we only compile "pure" QML types. any component-wrapped type is
+ // expected to appear through a binding
+ auto pureTypes = m_visitor->pureQmlTypes();
+ const QSet<QQmlJSScope::ConstPtr> types(pureTypes.begin(), pureTypes.end());
QmltcMethod urlMethod;
compileUrlMethod(urlMethod);
@@ -96,14 +99,16 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info, QmlIR::Document *doc)
if (isComponent(root)) {
compiledTypes.reserve(1);
compiledTypes.emplaceBack(); // create empty type
- const auto compile = [this](QmltcType &type, const CodeGenerator::CodeGenObject &object) {
- m_prototypeCodegen->compileQQmlComponentElements(type, object);
+ const auto compile = [this](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ const auto &object = m_prototypeCodegen->objectFromType(type);
+ m_prototypeCodegen->compileQQmlComponentElements(current, object);
};
Q_ASSERT(root == filteredObjects.at(0).type);
- m_prototypeCodegen->compileObject(compiledTypes.back(), filteredObjects.at(0), compile);
+ compileType(compiledTypes.back(), root, compile);
+ // m_prototypeCodegen->compileObject(compiledTypes.back(), filteredObjects.at(0), compile);
} else {
- const auto compile = [this](QmltcType &type, const CodeGenerator::CodeGenObject &object) {
- m_prototypeCodegen->compileObjectElements(type, object);
+ const auto compile = [this](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ compileTypeElements(current, type);
};
compiledTypes.reserve(filteredObjects.size());
@@ -112,7 +117,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info, QmlIR::Document *doc)
if (m_prototypeCodegen->ignoreObject(object))
continue;
compiledTypes.emplaceBack(); // create empty type
- m_prototypeCodegen->compileObject(compiledTypes.back(), object, compile);
+ compileType(compiledTypes.back(), object.type, compile);
}
}
if (hasErrors())
@@ -142,7 +147,9 @@ void QmltcCompiler::compileUrlMethod(QmltcMethod &urlMethod)
urlMethod.modifiers << u"noexcept"_qs;
}
-void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type)
+void QmltcCompiler::compileType(
+ QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements)
{
if (type->isSingleton()) {
recordError(type->sourceLocation(), u"Singleton types are not supported"_qs);
@@ -156,35 +163,49 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
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();
+ const bool isAnonymous = !documentRoot || type->internalName().at(0).isLower();
+
+ const auto hasQmlBase = [](const QQmlJSScope::ConstPtr &scope) {
+ if (!scope)
+ return false;
+ const auto base = scope->baseType();
+ if (!base)
+ return false;
+ return base->isComposite() && base->scopeType() == QQmlJSScope::QMLScope;
+ };
+ const bool baseTypeIsCompiledQml = hasQmlBase(type);
+
+ QmltcCodeGenerator generator { m_visitor };
current.baseClasses = { baseClass };
if (!documentRoot) {
// make document root a friend to allow it to access init and endInit
current.otherCode << u"friend class %1;"_qs.arg(rootType->internalName());
+
+ // additionally make an immediate parent a friend since that parent
+ // would create the object through a non-public constructor
+ const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
+ if (scope->isArrayScope())
+ return scope->parentScope();
+ return scope;
+ };
+ current.otherCode << u"friend class %1;"_qs.arg(
+ realQmlScope(type->parentScope())->internalName());
} else {
// 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->qmlTypes().size() > 0);
- QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount = m_visitor->qmlTypesWithQmlBases();
- QStringList typeCountComponents;
- typeCountComponents.reserve(1 + typesWithBaseTypeCount.size());
- // add this document's type counts minus document root
- typeCountComponents << QString::number(m_visitor->qmlTypes().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);
+ QmltcMethod typeCountMethod;
+ typeCountMethod.name = QmltcCodeGenerator::typeCountName;
+ typeCountMethod.returnType = u"uint"_qs;
+ typeCountMethod.body << u"return " + generator.generate_typeCount() + u";";
+ current.typeCount = typeCountMethod;
}
+ // make QQmltcObjectCreationHelper a friend of every type since it provides
+ // useful helper methods for all types
+ current.otherCode << u"friend class QT_PREPEND_NAMESPACE(QQmltcObjectCreationHelper);"_qs;
current.mocCode = {
u"Q_OBJECT"_qs,
@@ -194,31 +215,54 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
// add special member functions
current.baselineCtor.access = QQmlJSMetaMethod::Protected;
+ if (documentRoot) {
+ current.externalCtor.access = QQmlJSMetaMethod::Public;
+ } else {
+ current.externalCtor.access = QQmlJSMetaMethod::Protected;
+ }
current.init.access = QQmlJSMetaMethod::Protected;
+ current.beginClass.access = QQmlJSMetaMethod::Protected;
current.endInit.access = QQmlJSMetaMethod::Protected;
- current.externalCtor.access = QQmlJSMetaMethod::Public;
+ current.completeComponent.access = QQmlJSMetaMethod::Protected;
+ current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
+ current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
current.baselineCtor.name = current.cppType;
current.externalCtor.name = current.cppType;
- current.init.name = u"qmltc_init"_qs;
+ current.init.name = u"QML_init"_qs;
current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_qs;
- current.endInit.name = u"qmltc_finalize"_qs;
+ current.beginClass.name = u"QML_beginClass"_qs;
+ current.beginClass.returnType = u"void"_qs;
+ current.endInit.name = u"QML_endInit"_qs;
current.endInit.returnType = u"void"_qs;
-
+ current.completeComponent.name = u"QML_completeComponent"_qs;
+ current.completeComponent.returnType = u"void"_qs;
+ current.finalizeComponent.name = u"QML_finalizeComponent"_qs;
+ current.finalizeComponent.returnType = u"void"_qs;
+ current.handleOnCompleted.name = u"QML_handleOnCompleted"_qs;
+ current.handleOnCompleted.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.baselineCtor.parameterList = { parent };
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_qs, u"parentContext"_qs);
QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs);
+ current.baselineCtor.parameterList = { parent };
if (documentRoot) {
current.externalCtor.parameterList = { engine, parent };
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
+ current.beginClass.parameterList = { creator, finalizeFlag };
current.endInit.parameterList = { creator, engine, finalizeFlag };
+ current.completeComponent.parameterList = { creator, finalizeFlag };
+ current.finalizeComponent.parameterList = { creator, finalizeFlag };
+ current.handleOnCompleted.parameterList = { creator, finalizeFlag };
} else {
current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
+ current.beginClass.parameterList = { creator };
current.endInit.parameterList = { creator, engine };
+ current.completeComponent.parameterList = { creator };
+ current.finalizeComponent.parameterList = { creator };
+ current.handleOnCompleted.parameterList = { creator };
}
current.externalCtor.initializerList = { current.baselineCtor.name + u"(" + parent.name
@@ -233,8 +277,6 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
}
- QmltcCodeGenerator generator { rootType };
-
// compilation stub:
current.externalCtor.body << u"Q_UNUSED(engine);"_qs;
current.endInit.body << u"Q_UNUSED(engine);"_qs;
@@ -247,6 +289,7 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
type->internalName());
current.externalCtor.body
<< u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs;
+ current.externalCtor.body << u"creator.set(0, this);"_qs; // special case
// now call init
current.externalCtor.body << current.init.name
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
@@ -260,7 +303,35 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
+ u"(creator, engine, QQmlData::get(parent)->outerContext);";
}
- auto postponedGenerate = generator.generate_qmlContextSetup(current, type);
+ auto postponedQmlContextSetup = generator.generate_initCode(current, type);
+ auto postponedFinalizeCode = generator.generate_endInitCode(current, type);
+ generator.generate_beginClassCode(current, type);
+ generator.generate_completeComponentCode(current, type);
+ generator.generate_finalizeComponentCode(current, type);
+ generator.generate_handleOnCompletedCode(current, type);
+
+ compileElements(current, type);
+}
+
+void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type)
+{
+ const CodeGenerator::CodeGenObject &object = m_prototypeCodegen->objectFromType(type);
+
+ // TODO: make this a part of QmltcCodeGenerator::generate_endInitCode (and
+ // stop relying on QmlIR!)
+ if (object.irObject->flags & QV4::CompiledData::Object::HasDeferredBindings) {
+ current.endInit.body << u"{ // defer bindings"_qs;
+ current.endInit.body << u"auto ddata = QQmlData::get(this);"_qs;
+ current.endInit.body << u"auto thisContext = ddata->outerContext;"_qs;
+ current.endInit.body << u"Q_ASSERT(thisContext);"_qs;
+ current.endInit.body << QStringLiteral("ddata->deferData(%1, "
+ "QQmlEnginePrivate::get(engine)->"
+ "compilationUnitFromUrl(%2()), thisContext);")
+ .arg(QString::number(
+ m_prototypeCodegen->codegenObjectIndex(type)),
+ QmltcCodeGenerator::urlMethodName);
+ current.endInit.body << u"}"_qs;
+ }
// compile components of a type:
// - enums
@@ -273,13 +344,8 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
for (auto it = enums.begin(); it != enums.end(); ++it)
compileEnum(current, it.value());
- const auto methods = type->ownMethods();
auto properties = type->ownProperties().values();
- current.functions.reserve(methods.size() + properties.size() * 3); // sensible default
- for (const QQmlJSMetaMethod &m : methods)
- compileMethod(current, m);
-
- current.variables.reserve(properties.size());
+ current.properties.reserve(properties.size());
// Note: index() is the (future) meta property index, so make sure given
// properties are ordered by that index before compiling
std::sort(properties.begin(), properties.end(),
@@ -294,15 +360,44 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
continue;
}
if (p.isAlias()) {
- recordError(type->sourceLocation(), u"Property aliases are not supported"_qs);
+ m_prototypeCodegen->compileAlias(current, p, type);
} else {
compileProperty(current, p, type);
}
}
- const QMultiHash<QString, QQmlJSMetaPropertyBinding> allBindings = type->ownPropertyBindings();
- for (auto it = allBindings.begin(); it != allBindings.end(); ++it)
- compileBinding(current, it.value(), type, BindingAccessorData { type });
+ QHash<QString, const QmlIR::Function *> irFunctionsByName;
+ std::for_each(object.irObject->functionsBegin(), object.irObject->functionsEnd(),
+ [&](const QmlIR::Function &function) {
+ irFunctionsByName.insert(m_prototypeCodegen->stringAt(function.nameIndex),
+ std::addressof(function));
+ });
+ const auto methods = type->ownMethods();
+ current.functions.reserve(methods.size() + properties.size() * 3); // sensible default
+ for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
+ const QmlIR::Function *irFunction = irFunctionsByName.value(it.key(), nullptr);
+ m_prototypeCodegen->compileMethod(current, it.value(), irFunction, object);
+ }
+
+ {
+ const auto sortedBindings = CodeGenerator::toOrderedSequence(
+ object.irObject->bindingsBegin(), object.irObject->bindingsEnd(),
+ object.irObject->bindingCount());
+
+ auto scriptBindingsBegin =
+ std::find_if(sortedBindings.cbegin(), sortedBindings.cend(),
+ [](auto it) { return it->type == QmlIR::Binding::Type_Script; });
+ auto it = sortedBindings.cbegin();
+ for (; it != scriptBindingsBegin; ++it) {
+ m_prototypeCodegen->compileBinding(current, **it, object,
+ { object.type, u"this"_qs, u""_qs, false });
+ }
+
+ for (; it != sortedBindings.cend(); ++it) {
+ m_prototypeCodegen->compileBinding(current, **it, object,
+ { type, u"this"_qs, u""_qs, false });
+ }
+ }
}
void QmltcCompiler::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
@@ -412,34 +507,44 @@ void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty
mocPieces.reserve(10);
mocPieces << underlyingType << name;
+ QmltcPropertyData compilationData(p);
+
// 1. add setter and getter
- if (p.isWritable()) {
+ // If p.isList(), it's a QQmlListProperty. Then you can write the underlying list through
+ // the QQmlListProperty object retrieved with the getter. Setting it would make no sense.
+ if (p.isWritable() && !p.isList()) {
QmltcMethod setter {};
setter.returnType = u"void"_qs;
- setter.name = p.write();
- // QQmlJSAotVariable
+ setter.name = compilationData.write;
+ // QmltcVariable
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
u""_qs);
setter.body << variableName + u".setValue(" + name + u"_);";
+ setter.body << u"Q_EMIT " + compilationData.notify + u"();";
+ setter.userVisible = true;
current.functions.emplaceBack(setter);
mocPieces << u"WRITE"_qs << setter.name;
}
QmltcMethod getter {};
getter.returnType = underlyingType;
- getter.name = p.read();
+ getter.name = compilationData.read;
getter.body << u"return " + variableName + u".value();";
+ getter.userVisible = true;
current.functions.emplaceBack(getter);
mocPieces << u"READ"_qs << getter.name;
// 2. add bindable
- QmltcMethod bindable {};
- bindable.returnType = u"QBindable<" + underlyingType + u">";
- bindable.name = p.bindable();
- bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
- + u"));";
- current.functions.emplaceBack(bindable);
- mocPieces << u"BINDABLE"_qs << bindable.name;
+ if (!p.isList()) {
+ QmltcMethod bindable {};
+ bindable.returnType = u"QBindable<" + underlyingType + u">";
+ bindable.name = compilationData.bindable;
+ bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
+ + u"));";
+ bindable.userVisible = true;
+ current.functions.emplaceBack(bindable);
+ mocPieces << u"BINDABLE"_qs << bindable.name;
+ }
// 3. add/check notify (actually, this is already done inside QmltcVisitor)
@@ -455,7 +560,8 @@ void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty
current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_qs.arg(name);
// structure: (C++ type name, name, C++ class name, C++ signal name)
- current.properties.emplaceBack(underlyingType, variableName, current.cppType, p.notify());
+ current.properties.emplaceBack(underlyingType, variableName, current.cppType,
+ compilationData.notify);
}
void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
@@ -482,9 +588,7 @@ void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyB
// other errors, so the compiler just needs to add correct instructions,
// without if-checking every type
- QmltcCodeGenerator generator {
- QQmlJSScope::ConstPtr()
- }; // NB: we don't need document root here
+ QmltcCodeGenerator generator {};
switch (binding.bindingType()) {
case QQmlJSMetaPropertyBinding::BoolLiteral: {
@@ -520,7 +624,6 @@ void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyB
}
break;
}
-
// case QQmlJSMetaPropertyBinding::RegExpLiteral:
// case QQmlJSMetaPropertyBinding::Translation:
// case QQmlJSMetaPropertyBinding::TranslationById:
@@ -531,7 +634,7 @@ void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyB
// case QQmlJSMetaPropertyBinding::AttachedProperty:
// case QQmlJSMetaPropertyBinding::GroupProperty:
case QQmlJSMetaPropertyBinding::Invalid: {
- Q_UNREACHABLE(); // this is truly something that must not happen here
+ m_logger->log(u"This binding is invalid"_qs, Log_Compiler, binding.sourceLocation());
break;
}
default: {
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index 85b861e8de..c3e5bf317d 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -70,7 +70,10 @@ private:
QmltcCompilerInfo m_info {}; // miscellaneous input/output information
void compileUrlMethod(QmltcMethod &urlMethod);
- void compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type);
+ void
+ compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements);
+ void compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type);
void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m);
void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index ccee2c51a8..29adf4f2e5 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -50,8 +50,9 @@ struct QmltcCodeGenerator
{
static const QString privateEngineName;
static const QString urlMethodName;
+ static const QString typeCountName;
- QQmlJSScope::ConstPtr documentRoot;
+ QmltcVisitor *visitor = nullptr;
/*!
\internal
@@ -61,8 +62,22 @@ struct QmltcCodeGenerator
that have to be generated at a later point, once everything else is
compiled.
*/
- [[nodiscard]] inline decltype(auto) generate_qmlContextSetup(QmltcType &current,
- const QQmlJSScope::ConstPtr &type);
+ [[nodiscard]] inline decltype(auto) generate_initCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ [[nodiscard]] inline decltype(auto)
+ generate_endInitCode(QmltcType &current, const QQmlJSScope::ConstPtr &type) const;
+
+ inline void generate_interfaceCallCode(QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
+ const QString &interfaceName,
+ const QString &interfaceCall) const;
+ inline void generate_beginClassCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_completeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_finalizeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_handleOnCompletedCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
static void generate_assignToProperty(QStringList *block, const QQmlJSScope::ConstPtr &type,
const QQmlJSMetaProperty &p, const QString &value,
@@ -70,6 +85,13 @@ struct QmltcCodeGenerator
static void generate_setIdValue(QStringList *block, const QString &context, qsizetype index,
const QString &accessor, const QString &idString);
+ inline QString generate_typeCount() const
+ {
+ return generate_typeCount([](const QQmlJSScope::ConstPtr &) { return false; });
+ }
+ template<typename Predicate>
+ inline QString generate_typeCount(Predicate p) const;
+
static void generate_callExecuteRuntimeFunction(QStringList *block, const QString &url,
qsizetype index, const QString &accessor,
const QString &returnType,
@@ -82,6 +104,8 @@ struct QmltcCodeGenerator
const QQmlJSMetaProperty &p, int valueTypeIndex,
const QString &subTarget);
+ static inline void generate_getCompilationUnitFromUrl();
+
static std::tuple<QStringList, QString, QStringList>
wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value);
@@ -91,25 +115,40 @@ struct QmltcCodeGenerator
static QString wrap_addressof(const QString &addressed);
};
-inline decltype(auto)
-QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSScope::ConstPtr &type)
+inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
{
// qmltc_init()'s parameters:
// * QQmltcObjectCreationHelper* creator
// * QQmlEngine* engine
// * const QQmlRefPointer<QQmlContextData>& parentContext
// * bool canFinalize [optional, when document root]
- const bool isDocumentRoot = type == documentRoot;
+ const bool isDocumentRoot = type == visitor->result();
current.init.body << u"Q_UNUSED(creator);"_qs; // can happen sometimes
current.init.body << u"auto context = parentContext;"_qs;
- // if parent scope is a QML type and is not a (current) document root, the
- // parentContext we passed as input to this object is a context of another
- // document. we need to fix it by using parentContext->parent()
- if (auto parentScope = type->parentScope(); parentScope && parentScope->isComposite()
- && parentScope->scopeType() == QQmlJSScope::QMLScope && parentScope != documentRoot) {
+ // if parent scope has a QML base type and is not a (current) document root,
+ // the parentContext we passed as input to this object is a context of
+ // another document. we need to fix it by using parentContext->parent()
+
+ const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
+ if (scope->isArrayScope()) // TODO: it is special for some reason
+ return scope->parentScope();
+ return scope;
+ };
+ const auto hasQmlBase = [](const QQmlJSScope::ConstPtr &scope) {
+ if (!scope)
+ return false;
+ const auto base = scope->baseType();
+ if (!base)
+ return false;
+ return base->isComposite() && base->scopeType() == QQmlJSScope::QMLScope;
+ };
+
+ if (auto parentScope = realQmlScope(type->parentScope());
+ parentScope != visitor->result() && hasQmlBase(parentScope)) {
current.init.body << u"// NB: context->parent() is the context of this document"_qs;
current.init.body << u"context = context->parent();"_qs;
}
@@ -122,9 +161,20 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
if (isDocumentRoot)
lhs = u"context = "_qs;
current.init.body << u"// 0. call base's init method"_qs;
- current.init.body << QStringLiteral(
- "%1%2::%3(creator, engine, context, /* finalize */ false)")
- .arg(lhs, base->internalName(), current.init.name);
+
+ Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset = generate_typeCount(isCurrentType);
+
+ current.init.body << u"{"_qs;
+ current.init.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_qs.arg(
+ creationOffset);
+ current.init.body
+ << QStringLiteral("%1%2::%3(&subCreator, engine, context, /* finalize */ false);")
+ .arg(lhs, base->internalName(), current.init.name);
+ current.init.body << u"}"_qs;
}
current.init.body
@@ -159,20 +209,26 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
current.init.body << u"context->setContextObject(this);"_qs;
}
- if (int id = type->runtimeId(); id >= 0) {
+ if (int id = visitor->runtimeId(type); id >= 0) {
current.init.body << u"// 3. set id since it is provided"_qs;
QmltcCodeGenerator::generate_setIdValue(&current.init.body, u"context"_qs, id, u"this"_qs,
u"<unknown>"_qs);
}
- // TODO: add QQmlParserStatus::classBegin() to init
-
const auto generateFinalLines = [&current, isDocumentRoot]() {
if (isDocumentRoot) {
current.init.body << u"// 4. call finalize in the document root"_qs;
current.init.body << u"if (canFinalize) {"_qs;
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.beginClass.name);
current.init.body << QStringLiteral(" %1(creator, engine, /* finalize */ true);")
.arg(current.endInit.name);
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.completeComponent.name);
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.finalizeComponent.name);
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.handleOnCompleted.name);
current.init.body << u"}"_qs;
}
current.init.body << u"return context;"_qs;
@@ -181,6 +237,195 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
return QScopeGuard(generateFinalLines);
}
+inline decltype(auto)
+QmltcCodeGenerator::generate_endInitCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ // QML_endInit()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * QQmlEngine* engine
+ // * bool canFinalize [optional, when document root]
+ const bool isDocumentRoot = type == visitor->result();
+ current.endInit.body << u"Q_UNUSED(creator);"_qs;
+ current.endInit.body << u"Q_UNUSED(engine);"_qs;
+ if (isDocumentRoot)
+ current.endInit.body << u"Q_UNUSED(canFinalize);"_qs;
+
+ if (auto base = type->baseType(); base->isComposite()) {
+ current.endInit.body << u"// call base's finalize method"_qs;
+ Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset = generate_typeCount(isCurrentType);
+ current.endInit.body << u"{"_qs;
+ current.endInit.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_qs.arg(
+ creationOffset);
+ current.endInit.body << u"%1::%2(&subCreator, engine, /* finalize */ false);"_qs.arg(
+ base->internalName(), current.endInit.name);
+ current.endInit.body << u"}"_qs;
+ }
+
+ const auto generateFinalLines = [&current, isDocumentRoot, this]() {
+ if (!isDocumentRoot) // document root does all the work here
+ return;
+
+ const auto types = visitor->pureQmlTypes();
+ current.endInit.body << u"// finalize children"_qs;
+ for (qsizetype i = 1; i < types.size(); ++i) {
+ const auto &type = types[i];
+ Q_ASSERT(!type->isComponentRootElement());
+ current.endInit.body << u"creator->get<%1>(%2)->%3(creator, engine);"_qs.arg(
+ type->internalName(), QString::number(i), current.endInit.name);
+ }
+ };
+ return QScopeGuard(generateFinalLines);
+}
+
+inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function,
+ const QQmlJSScope::ConstPtr &type,
+ const QString &interfaceName,
+ const QString &interfaceCall) const
+{
+ // function's parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * bool canFinalize [optional, when document root]
+ const bool isDocumentRoot = type == visitor->result();
+ function->body << u"Q_UNUSED(creator);"_qs;
+ if (isDocumentRoot)
+ function->body << u"Q_UNUSED(canFinalize);"_qs;
+
+ if (auto base = type->baseType(); base->isComposite()) {
+ function->body << u"// call base's method"_qs;
+ Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset = generate_typeCount(isCurrentType);
+ function->body << u"{"_qs;
+ function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_qs.arg(
+ creationOffset);
+ function->body << u"%1::%2(&subCreator, /* finalize */ false);"_qs.arg(base->internalName(),
+ function->name);
+ function->body << u"}"_qs;
+ }
+
+ if (!isDocumentRoot)
+ return;
+
+ const auto types = visitor->pureQmlTypes();
+ function->body << u"// call children's methods"_qs;
+ for (qsizetype i = 1; i < types.size(); ++i) {
+ const auto &type = types[i];
+ Q_ASSERT(!type->isComponentRootElement());
+ function->body << u"{"_qs;
+ function->body << u"auto child = creator->get<%1>(%2);"_qs.arg(type->internalName(),
+ QString::number(i));
+ function->body << u"child->%1(creator);"_qs.arg(function->name);
+ if (type->hasInterface(interfaceName)) {
+ function->body << u"Q_ASSERT(dynamic_cast<%1 *>(child) != nullptr);"_qs.arg(
+ interfaceName);
+ function->body << u"child->%1();"_qs.arg(interfaceCall);
+ }
+ function->body << u"}"_qs;
+ }
+
+ if (type->hasInterface(interfaceName)) {
+ function->body << u"if (canFinalize) {"_qs;
+ function->body << u" // call own method"_qs;
+ function->body << u" this->%1();"_qs.arg(interfaceCall);
+ function->body << u"}"_qs;
+ }
+}
+
+inline void QmltcCodeGenerator::generate_beginClassCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ generate_interfaceCallCode(&current.beginClass, type, u"QQmlParserStatus"_qs, u"classBegin"_qs);
+}
+
+inline void
+QmltcCodeGenerator::generate_completeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ generate_interfaceCallCode(&current.completeComponent, type, u"QQmlParserStatus"_qs,
+ u"componentComplete"_qs);
+}
+
+inline void
+QmltcCodeGenerator::generate_finalizeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ generate_interfaceCallCode(&current.finalizeComponent, type, u"QQmlFinalizerHook"_qs,
+ u"componentFinalized"_qs);
+}
+
+inline void
+QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ // QML_handleOnCompleted()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * bool canFinalize [optional, when document root]
+ const bool isDocumentRoot = type == visitor->result();
+ current.handleOnCompleted.body << u"Q_UNUSED(creator);"_qs;
+ if (isDocumentRoot)
+ current.handleOnCompleted.body << u"Q_UNUSED(canFinalize);"_qs;
+
+ if (auto base = type->baseType(); base->isComposite()) {
+ current.handleOnCompleted.body << u"// call base's method"_qs;
+ Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset = generate_typeCount(isCurrentType);
+ current.handleOnCompleted.body << u"{"_qs;
+ current.handleOnCompleted.body
+ << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_qs.arg(creationOffset);
+ current.handleOnCompleted.body << u"%1::%2(&subCreator, /* finalize */ false);"_qs.arg(
+ base->internalName(), current.handleOnCompleted.name);
+ current.handleOnCompleted.body << u"}"_qs;
+ }
+
+ if (!isDocumentRoot) // document root does all the work here
+ return;
+
+ const auto types = visitor->pureQmlTypes();
+ current.handleOnCompleted.body << u"// call children's methods"_qs;
+ for (qsizetype i = 1; i < types.size(); ++i) {
+ const auto &type = types[i];
+ Q_ASSERT(!type->isComponentRootElement());
+ current.handleOnCompleted.body << u"creator->get<%1>(%2)->%3(creator);"_qs.arg(
+ type->internalName(), QString::number(i), current.handleOnCompleted.name);
+ }
+}
+
+template<typename Predicate>
+inline QString QmltcCodeGenerator::generate_typeCount(Predicate p) const
+{
+ const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount = visitor->qmlTypesWithQmlBases();
+ QStringList components;
+ components.reserve(1 + typesWithBaseTypeCount.size());
+
+ // add this document's type counts minus document root
+ Q_ASSERT(visitor->pureQmlTypes().size() > 0);
+ components << QString::number(visitor->pureQmlTypes().size() - 1);
+
+ // traverse types with QML base classes
+ for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) {
+ if (p(t))
+ break;
+ QString typeCountTemplate = u"QQmltcObjectCreationHelper::typeCount<%1>()"_qs;
+ if (t == visitor->result()) { // t is this document's root
+ components << typeCountTemplate.arg(t->baseTypeName());
+ } else {
+ components << typeCountTemplate.arg(t->internalName());
+ }
+ }
+
+ return components.join(u" + "_qs);
+}
+
QT_END_NAMESPACE
#endif // QMLTCCOMPILERPIECES_H
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 0a3e7617bd..1e586490a0 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -137,9 +137,10 @@ struct QmltcType
QmltcCtor baselineCtor {}; // does basic contruction
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 completeComponent {}; // calls componentComplete()
- QmltcMethod finalizeComponent {}; // calls componentFinalized()
+ QmltcMethod completeComponent {}; // calls QQmlParserStatus::componentComplete()
+ QmltcMethod finalizeComponent {}; // calls QQmlFinalizerHook::componentFinalized()
QmltcMethod handleOnCompleted {}; // calls Component.onCompleted
std::optional<QmltcDtor> dtor {};
@@ -151,7 +152,7 @@ struct QmltcType
QList<QmltcProperty> properties;
// QML document root specific:
- std::optional<QmltcVariable> typeCount; // the number of QML types defined in a document
+ std::optional<QmltcMethod> typeCount; // the number of QML types defined in a document
// TODO: only needed for binding callables - should not be needed, generally
bool ignoreInit = false; // specifies whether init and externalCtor should be ignored
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index b70e3fe329..213acb89f6 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -29,6 +29,7 @@
#include "qmltcvisitor.h"
#include <QtCore/qfileinfo.h>
+#include <QtCore/qstack.h>
#include <algorithm>
@@ -43,6 +44,24 @@ static QString uniqueNameFromPieces(const QStringList &pieces, QHash<QString, in
return possibleName;
}
+static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
+{
+ Q_ASSERT(type->isComposite()); // we're dealing with composite types here
+ for (; type; type = type->parentScope()) {
+ if (type->isWrappedInImplicitComponent())
+ return true;
+ // for a composite type, its internalName() is guaranteed to not be a
+ // QQmlComponent. we need to detect a case with `Component {}` QML type
+ // where the *immediate* base type of the current type will be the
+ // QQmlComponent. note that non-composite base is different: if our type
+ // is not a direct child of QQmlComponent, then it is a good type that
+ // we have to compile
+ if (auto base = type->baseType(); base && base->internalName() == u"QQmlComponent")
+ return true;
+ }
+ return false;
+}
+
QmltcVisitor::QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory, const QStringList &qmldirFiles)
: QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmldirFiles)
@@ -90,7 +109,7 @@ void QmltcVisitor::findCppIncludes()
};
// walk the whole type hierarchy
- for (const QQmlJSScope::ConstPtr &type : qAsConst(m_qmlTypes)) {
+ for (const QQmlJSScope::ConstPtr &type : qAsConst(m_pureQmlTypes)) {
// TODO: figure how to NOT walk all the types. theoretically, we can
// stop at first non-composite type
for (auto t = type; t; t = t->baseType()) {
@@ -142,15 +161,18 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
// give C++-relevant internal names to QMLScopes, we can use them later in compiler
m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
- if (auto base = m_currentScope->baseType(); base && base->isComposite())
+ if (auto base = m_currentScope->baseType();
+ base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
m_qmlTypesWithQmlBases.append(m_currentScope);
+ }
return true;
}
void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object)
{
- m_qmlTypeNames.removeLast();
+ if (m_currentScope->scopeType() == QQmlJSScope::QMLScope)
+ m_qmlTypeNames.removeLast();
QQmlJSImportVisitor::endVisit(object);
}
@@ -167,8 +189,10 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
// give C++-relevant internal names to QMLScopes, we can use them later in compiler
m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
- if (auto base = m_currentScope->baseType(); base && base->isComposite())
+ if (auto base = m_currentScope->baseType();
+ base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
m_qmlTypesWithQmlBases.append(m_currentScope);
+ }
return true;
}
@@ -215,6 +239,20 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
return true;
}
+bool QmltcVisitor::visit(QQmlJS::AST::UiScriptBinding *scriptBinding)
+{
+ if (!QQmlJSImportVisitor::visit(scriptBinding))
+ return false;
+
+ {
+ const auto id = scriptBinding->qualifiedId;
+ if (!id->next && id->name == QLatin1String("id"))
+ m_typesWithId[m_currentScope] = -1; // temporary value
+ }
+
+ return true;
+}
+
bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component)
{
if (!QQmlJSImportVisitor::visit(component))
@@ -229,8 +267,186 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component)
void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
{
QQmlJSImportVisitor::endVisit(program);
+ if (!m_exportedRootScope) // in case we failed badly
+ return;
+
+ QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> bindings;
+ for (const QQmlJSScope::ConstPtr &type : qAsConst(m_qmlTypes)) {
+ if (isOrUnderComponent(type))
+ continue;
+ bindings.insert(type, type->ownPropertyBindingsInQmlIROrder());
+ }
+ postVisitResolve(bindings);
findCppIncludes();
}
+QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
+{
+ switch (binding.bindingType()) {
+ case QQmlJSMetaPropertyBinding::Object:
+ return binding.objectType();
+ case QQmlJSMetaPropertyBinding::Interceptor:
+ return binding.interceptorType();
+ case QQmlJSMetaPropertyBinding::ValueSource:
+ return binding.valueSourceType();
+ // TODO: AttachedProperty and GroupProperty are not supported yet,
+ // but have to also be acknowledged here
+ default:
+ return {};
+ }
+ return {};
+}
+
+template<typename Predicate>
+void iterateTypes(
+ const QQmlJSScope::ConstPtr &root,
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings,
+ Predicate predicate)
+{
+ // NB: depth-first-search is used here to mimic various QmlIR passes
+ QStack<QQmlJSScope::ConstPtr> types;
+ types.push(root);
+ while (!types.isEmpty()) {
+ auto current = types.pop();
+
+ if (predicate(current))
+ continue;
+
+ if (isOrUnderComponent(current)) // ignore implicit/explicit components
+ continue;
+
+ Q_ASSERT(qmlIrOrderedBindings.contains(current));
+ const auto &bindings = qmlIrOrderedBindings[current];
+ // reverse the binding order here, because stack processes right-most
+ // child first and we need left-most first
+ for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
+ const auto &binding = *it;
+ if (auto type = fetchType(binding))
+ types.push(type);
+ }
+ }
+}
+
+template<typename Predicate>
+void iterateBindings(
+ const QQmlJSScope::ConstPtr &root,
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings,
+ Predicate predicate)
+{
+ // NB: depth-first-search is used here to mimic various QmlIR passes
+ QStack<QQmlJSScope::ConstPtr> types;
+ types.push(root);
+ while (!types.isEmpty()) {
+ auto current = types.pop();
+
+ if (isOrUnderComponent(current)) // ignore implicit/explicit components
+ continue;
+
+ Q_ASSERT(qmlIrOrderedBindings.contains(current));
+ const auto &bindings = qmlIrOrderedBindings[current];
+ // reverse the binding order here, because stack processes right-most
+ // child first and we need left-most first
+ for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
+ const auto &binding = *it;
+
+ if (predicate(current, binding))
+ continue;
+
+ if (auto type = fetchType(binding))
+ types.push(type);
+ }
+ }
+}
+
+void QmltcVisitor::postVisitResolve(
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings)
+{
+ // This is a special function that must be called after
+ // QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
+ // resolve things that couldn't be resolved during the AST traversal, such
+ // as anything that is dependent on implicit or explicit components
+
+ // 1. find types that are part of the deferred bindings (we care about
+ // *types* exclusively here)
+ QSet<QQmlJSScope::ConstPtr> deferredTypes;
+ const auto findDeferred = [&deferredTypes](const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaPropertyBinding &binding) {
+ if (binding.hasObject() || binding.hasInterceptor() || binding.hasValueSource()) {
+ const QString propertyName = binding.propertyName();
+ Q_ASSERT(!propertyName.isEmpty());
+ if (type->isNameDeferred(propertyName)) {
+ deferredTypes.insert(fetchType(binding));
+ return true;
+ }
+ }
+ return false;
+ };
+ iterateBindings(m_exportedRootScope, qmlIrOrderedBindings, findDeferred);
+
+ const auto isOrUnderDeferred = [&deferredTypes](QQmlJSScope::ConstPtr type) {
+ for (; type; type = type->parentScope()) {
+ if (deferredTypes.contains(type))
+ return true;
+ }
+ return false;
+ };
+
+ // 2. find all "pure" QML types
+ m_pureQmlTypes.reserve(m_qmlTypes.size());
+ for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
+ const QQmlJSScope::ConstPtr &type = m_qmlTypes.at(i);
+
+ if (isOrUnderComponent(type) || isOrUnderDeferred(type)) {
+ // root is special: we compile Component roots. root is also never
+ // deferred, so in case `isOrUnderDeferred(type)` returns true, we
+ // always continue here
+ if (type != m_exportedRootScope)
+ continue;
+ }
+
+ m_pureTypeIndices[type] = m_pureQmlTypes.size();
+ m_pureQmlTypes.append(type);
+ }
+
+ // filter out deferred types
+ {
+ QList<QQmlJSScope::ConstPtr> filteredQmlTypesWithQmlBases;
+ filteredQmlTypesWithQmlBases.reserve(m_qmlTypesWithQmlBases.size());
+ std::copy_if(m_qmlTypesWithQmlBases.cbegin(), m_qmlTypesWithQmlBases.cend(),
+ std::back_inserter(filteredQmlTypesWithQmlBases),
+ [&](const QQmlJSScope::ConstPtr &type) { return !isOrUnderDeferred(type); });
+ qSwap(m_qmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
+ }
+
+ // 3. figure synthetic indices of QQmlComponent-wrapped types
+ int syntheticCreationIndex = -1;
+ const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) mutable {
+ if (type->isComponentRootElement()) {
+ m_syntheticTypeIndices[type] = ++syntheticCreationIndex;
+ return true;
+ }
+ return false;
+ };
+ iterateTypes(m_exportedRootScope, qmlIrOrderedBindings, addSyntheticIndex);
+
+ // 4. figure runtime object ids for non-component wrapped types
+ int currentId = 0;
+ const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) mutable {
+ // fancy way to call type->isComponentRootElement(). any type that is
+ // considered synthetic shouldn't be processed here. even if it has id,
+ // it doesn't need to be set by qmltc
+ if (m_syntheticTypeIndices.contains(type))
+ return true;
+
+ if (m_typesWithId.contains(type))
+ m_typesWithId[type] = currentId++;
+
+ // otherwise we need to `return true` here
+ Q_ASSERT(!type->isInlineComponent());
+ return false;
+ };
+ iterateTypes(m_exportedRootScope, qmlIrOrderedBindings, setRuntimeId);
+}
+
QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
index f3a4babbda..b3ba6e91aa 100644
--- a/tools/qmltc/qmltcvisitor.h
+++ b/tools/qmltc/qmltcvisitor.h
@@ -42,6 +42,8 @@ QT_BEGIN_NAMESPACE
class QmltcVisitor : public QQmlJSImportVisitor
{
void findCppIncludes();
+ void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>>
+ &qmlIrOrderedBindings);
public:
QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
@@ -54,6 +56,8 @@ public:
bool visit(QQmlJS::AST::UiObjectBinding *) override;
void endVisit(QQmlJS::AST::UiObjectBinding *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *) override;
+
bool visit(QQmlJS::AST::UiPublicMember *) override;
bool visit(QQmlJS::AST::UiInlineComponent *) override;
@@ -63,11 +67,59 @@ public:
QList<QQmlJSScope::ConstPtr> qmlTypesWithQmlBases() const { return m_qmlTypesWithQmlBases; }
QSet<QString> cppIncludeFiles() const { return m_cppIncludes; }
+ qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(m_pureTypeIndices.contains(type));
+ Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ return m_pureTypeIndices[type];
+ }
+
+ qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(m_syntheticTypeIndices.contains(type));
+ Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->isComponentRootElement());
+ return m_syntheticTypeIndices[type] + qmlTypes().size();
+ }
+
+ /*! \internal
+ Returns a runtime index counterpart of `id: foo` for \a type. Returns -1
+ if \a type does not have an id.
+ */
+ int runtimeId(const QQmlJSScope::ConstPtr &type) const
+ {
+ // NB: this function is expected to be called for "pure" types
+ Q_ASSERT(!m_typesWithId.contains(type) || m_typesWithId[type] != -1);
+ return m_typesWithId.value(type, -1);
+ }
+
+ /*! \internal
+ Returns all encountered QML types.
+ */
+ QList<QQmlJSScope::ConstPtr> allQmlTypes() const { return qmlTypes(); }
+
+ /*! \internal
+ Returns encountered QML types which return \c false in
+ \c{isComponentRootElement()}. Called "pure", because these are the ones
+ that are not wrapped into QQmlComponent. Pure QML types can be created
+ through direct constructor invocation.
+ */
+ QList<QQmlJSScope::ConstPtr> pureQmlTypes() const { return m_pureQmlTypes; }
+
protected:
QStringList m_qmlTypeNames; // names of QML types arranged as a stack
QHash<QString, int> m_qmlTypeNameCounts;
QList<QQmlJSScope::ConstPtr> m_qmlTypesWithQmlBases; // QML types with composite/QML base types
QSet<QString> m_cppIncludes; // all C++ includes found from QQmlJSScope hierarchy
+ QList<QQmlJSScope::ConstPtr> m_pureQmlTypes; // the ones not under QQmlComponent
+
+ QHash<QQmlJSScope::ConstPtr, qsizetype> m_pureTypeIndices;
+ QHash<QQmlJSScope::ConstPtr, qsizetype> m_syntheticTypeIndices;
+
+ // prefer allQmlTypes or pureQmlTypes. this function is misleading in qmltc
+ QList<QQmlJSScope::ConstPtr> qmlTypes() const { return QQmlJSImportVisitor::qmlTypes(); }
+
+ QHash<QQmlJSScope::ConstPtr, int> m_typesWithId;
};
QT_END_NAMESPACE