aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmltc')
-rw-r--r--tools/qmltc/CMakeLists.txt2
-rw-r--r--tools/qmltc/main.cpp55
-rw-r--r--tools/qmltc/qmltccodewriter.cpp99
-rw-r--r--tools/qmltc/qmltccodewriter.h4
-rw-r--r--tools/qmltc/qmltccompiler.cpp397
-rw-r--r--tools/qmltc/qmltccompiler.h8
-rw-r--r--tools/qmltc/qmltccompilerpieces.cpp17
-rw-r--r--tools/qmltc/qmltccompilerpieces.h27
-rw-r--r--tools/qmltc/qmltcoutputir.h46
-rw-r--r--tools/qmltc/qmltcpropertyutils.h11
-rw-r--r--tools/qmltc/qmltctyperesolver.cpp5
-rw-r--r--tools/qmltc/qmltcvisitor.cpp79
-rw-r--r--tools/qmltc/qmltcvisitor.h8
13 files changed, 613 insertions, 145 deletions
diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt
index 8e7ce0a586..42e47e24da 100644
--- a/tools/qmltc/CMakeLists.txt
+++ b/tools/qmltc/CMakeLists.txt
@@ -3,7 +3,7 @@
qt_get_tool_target_name(target_name qmltc)
qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Type Compiler"
+ TARGET_DESCRIPTION "QML type compiler"
TOOLS_TARGET Qml
SOURCES
main.cpp
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index 6ef0afe105..1899f4087e 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -9,6 +9,7 @@
#include <private/qqmljscompiler_p.h>
#include <private/qqmljsresourcefilemapper_p.h>
+#include <private/qqmljsutils_p.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qurl.h>
@@ -24,6 +25,8 @@
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
+#include <QtQmlCompiler/private/qqmljsliteralbindingcheck_p.h>
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
@@ -31,8 +34,8 @@ using namespace Qt::StringLiterals;
void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
{
- for (const QQmlJSLogger::Category &category : logger.categories()) {
- if (category == qmlUnusedImports)
+ for (const QQmlJS::LoggerCategory &category : logger.categories()) {
+ if (category.id() == qmlUnusedImports)
continue;
logger.setCategoryLevel(category.id(), QtCriticalMsg);
logger.setCategoryIgnored(category.id(), false);
@@ -99,6 +102,25 @@ int main(int argc, char **argv)
QCoreApplication::translate("main", "namespace")
};
parser.addOption(namespaceOption);
+ QCommandLineOption moduleOption{
+ u"module"_s,
+ QCoreApplication::translate("main",
+ "Name of the QML module that this QML code belongs to."),
+ QCoreApplication::translate("main", "module")
+ };
+ parser.addOption(moduleOption);
+ QCommandLineOption exportOption{ u"export"_s,
+ QCoreApplication::translate(
+ "main", "Export macro used in the generated C++ code"),
+ QCoreApplication::translate("main", "export") };
+ parser.addOption(exportOption);
+ QCommandLineOption exportIncludeOption{
+ u"exportInclude"_s,
+ QCoreApplication::translate(
+ "main", "Header defining the export macro to be used in the generated C++ code"),
+ QCoreApplication::translate("main", "exportInclude")
+ };
+ parser.addOption(exportIncludeOption);
parser.process(app);
@@ -153,7 +175,7 @@ int main(int argc, char **argv)
if (!parser.isSet(bareOption))
importPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
- QStringList qmldirFiles = parser.values(qmldirOption);
+ QStringList qmldirFiles = QQmlJSUtils::cleanPaths(parser.values(qmldirOption));
QString outputCppFile;
if (!parser.isSet(outputCppOption)) {
@@ -224,6 +246,8 @@ int main(int argc, char **argv)
info.outputHFile = parser.value(outputHOption);
info.resourcePath = firstQml(paths);
info.outputNamespace = parser.value(namespaceOption);
+ info.exportMacro = parser.value(exportOption);
+ info.exportInclude = parser.value(exportIncludeOption);
if (info.outputCppFile.isEmpty()) {
fprintf(stderr, "An output C++ file is required. Pass one using --impl");
@@ -236,24 +260,37 @@ int main(int argc, char **argv)
QQmlJSImporter importer { importPaths, &mapper };
importer.setMetaDataMapper(&metaDataMapper);
- auto createQmltcVisitor = [](const QQmlJSScope::Ptr &root, QQmlJSImporter *importer,
- QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmldirFiles) -> QQmlJSImportVisitor * {
- return new QmltcVisitor(root, importer, logger, implicitImportDirectory, qmldirFiles);
+ auto qmltcVisitor = [](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
+ const QQmlJSImporter::ImportVisitorPrerequisites &p) {
+ QmltcVisitor v(p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles);
+ QQmlJS::AST::Node::accept(rootNode, &v);
};
- importer.setImportVisitorCreator(createQmltcVisitor);
+ importer.setImportVisitor(qmltcVisitor);
QQmlJSLogger logger;
logger.setFileName(url);
logger.setCode(sourceCode);
setupLogger(logger);
- QmltcVisitor visitor(QQmlJSScope::create(), &importer, &logger,
+ auto currentScope = QQmlJSScope::create();
+ if (parser.isSet(moduleOption))
+ currentScope->setOwnModuleName(parser.value(moduleOption));
+
+ QmltcVisitor visitor(currentScope, &importer, &logger,
QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);
visitor.setMode(QmltcVisitor::Compile);
QmltcTypeResolver typeResolver { &importer };
typeResolver.init(&visitor, qmlParser.rootNode());
+ using PassManagerPtr =
+ std::unique_ptr<QQmlSA::PassManager,
+ decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+ PassManagerPtr passMan(QQmlSA::PassManagerPrivate::createPassManager(&visitor, &typeResolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+ passMan->registerPropertyPass(std::make_unique<QQmlJSLiteralBindingCheck>(passMan.get()),
+ QString(), QString(), QString());
+ passMan->analyze(QQmlJSScope::createQQmlSAElement(visitor.result()));
+
if (logger.hasErrors())
return EXIT_FAILURE;
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index 8010f1066c..134de0f98e 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -42,14 +42,14 @@ static QString getFunctionCategory(const QmltcMethod &method)
{
QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method));
switch (method.type) {
- case QQmlJSMetaMethod::Signal:
+ case QQmlJSMetaMethodType::Signal:
category = u"Q_SIGNALS"_s;
break;
- case QQmlJSMetaMethod::Slot:
+ case QQmlJSMetaMethodType::Slot:
category += u" Q_SLOTS"_s;
break;
- case QQmlJSMetaMethod::Method:
- case QQmlJSMetaMethod::StaticMethod:
+ case QQmlJSMetaMethodType::Method:
+ case QQmlJSMetaMethodType::StaticMethod:
break;
}
return category;
@@ -118,6 +118,7 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
code.rawAppendToHeader(u"#include <QtCore/qproperty.h>");
code.rawAppendToHeader(u"#include <QtCore/qobject.h>");
code.rawAppendToHeader(u"#include <QtCore/qcoreapplication.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qxpfunctional.h>");
code.rawAppendToHeader(u"#include <QtQml/qqmlengine.h>");
code.rawAppendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
code.rawAppendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
@@ -160,6 +161,64 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
}
}
+void QmltcCodeWriter::write(QmltcOutputWrapper &code,
+ const QmltcPropertyInitializer &propertyInitializer,
+ const QmltcType &wrappedType)
+{
+ code.rawAppendToHeader(u"class " + propertyInitializer.name + u" {");
+
+ {
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ code.rawAppendToHeader(u"friend class " + wrappedType.cppType + u";");
+ }
+
+ code.rawAppendToHeader(u"public:"_s);
+
+ [[maybe_unused]] QmltcOutputWrapper::MemberNameScope typeScope(&code, propertyInitializer.name);
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.constructor);
+ code.rawAppendToHeader(u""); // blank line
+
+ for (const auto &propertySetter : propertyInitializer.propertySetters) {
+ write(code, propertySetter);
+ }
+ }
+
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"private:"_s);
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.component);
+ write(code, propertyInitializer.initializedCache);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle)
+{
+ code.rawAppendToHeader(u"struct " + requiredPropertiesBundle.name + u" {");
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ for (const auto &member : requiredPropertiesBundle.members) {
+ write(code, member);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
void QmltcCodeWriter::writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &outNamespace)
{
@@ -187,12 +246,14 @@ static void writeToFile(const QString &path, const QByteArray &data)
QFileInfo fi(path);
if (fi.exists() && fi.size() == data.size()) {
QFile oldFile(path);
- oldFile.open(QIODevice::ReadOnly);
- if (oldFile.readAll() == data)
- return;
+ if (oldFile.open(QIODevice::ReadOnly)) {
+ if (oldFile.readAll() == data)
+ return;
+ }
}
QFile file(path);
- file.open(QIODevice::WriteOnly);
+ if (!file.open(QIODevice::WriteOnly))
+ qFatal("Could not open file %s", qPrintable(path));
file.write(data);
}
@@ -209,7 +270,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
code.rawAppendToHeader(u"class " + type.cppType + u";");
// write all the types and their content
for (const QmltcType &type : std::as_const(program.compiledTypes))
- write(code, type);
+ write(code, type, program.exportMacro);
// add typeCount definitions. after all types have been written down (so
// they are now complete types as per C++). practically, this only concerns
@@ -251,10 +312,14 @@ static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &fu
}
}
-void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
+ const QString &exportMacro)
{
const auto constructClassString = [&]() {
- QString str = u"class " + type.cppType;
+ QString str = u"class "_s;
+ if (!exportMacro.isEmpty())
+ str.append(exportMacro).append(u" "_s);
+ str.append(type.cppType);
QStringList nonEmptyBaseClasses;
nonEmptyBaseClasses.reserve(type.baseClasses.size());
std::copy_if(type.baseClasses.cbegin(), type.baseClasses.cend(),
@@ -287,6 +352,12 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
code.rawAppendToHeader(u"/* External C++ API */");
code.rawAppendToHeader(u"public:", -1);
+ if (!type.propertyInitializer.name.isEmpty())
+ write(code, type.propertyInitializer, type);
+
+ if (type.requiredPropertiesBundle)
+ write(code, *type.requiredPropertiesBundle);
+
// NB: when non-document root, the externalCtor won't be public - but we
// really don't care about the output format of such types
if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) {
@@ -340,7 +411,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
// children
for (const auto &child : std::as_const(type.children))
- QmltcCodeWriter::write(code, child);
+ QmltcCodeWriter::write(code, child, exportMacro);
// (non-visible) functions
dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
@@ -392,14 +463,14 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
const auto [hSignature, cppSignature] = functionSignatures(method);
// Note: augment return type with preambles in declaration
- code.rawAppendToHeader((method.type == QQmlJSMetaMethod::StaticMethod
+ code.rawAppendToHeader((method.type == QQmlJSMetaMethodType::StaticMethod
? u"static " + functionReturnType(method)
: functionReturnType(method))
+ u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = method.type;
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
code.rawAppendToCpp(u""_s); // blank line
if (method.comments.size() > 0) {
code.rawAppendToCpp(u"/*! \\internal"_s);
diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h
index e95777998e..20b0262737 100644
--- a/tools/qmltc/qmltccodewriter.h
+++ b/tools/qmltc/qmltccodewriter.h
@@ -20,13 +20,15 @@ struct QmltcCodeWriter
static void writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &outNamespace);
static void write(QmltcOutputWrapper &code, const QmltcProgram &program);
- static void write(QmltcOutputWrapper &code, const QmltcType &type);
+ static void write(QmltcOutputWrapper &code, const QmltcType &type, const QString &exportMacro);
static void write(QmltcOutputWrapper &code, const QmltcEnum &enumeration);
static void write(QmltcOutputWrapper &code, const QmltcMethod &method);
static void write(QmltcOutputWrapper &code, const QmltcCtor &ctor);
static void write(QmltcOutputWrapper &code, const QmltcDtor &dtor);
static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
static void write(QmltcOutputWrapper &code, const QmltcProperty &prop);
+ static void write(QmltcOutputWrapper &code, const QmltcPropertyInitializer &propertyInitializer, const QmltcType& wrappedType);
+ static void write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle);
private:
static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 4050136ef2..75bd580e07 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -8,6 +8,7 @@
#include "qmltccompilerpieces.h"
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qqmljsutils_p.h>
#include <algorithm>
@@ -22,6 +23,137 @@ bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
return false;
}
+static QList<QQmlJSMetaProperty> unboundRequiredProperties(
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+ QList<QQmlJSMetaProperty> requiredProperties{};
+
+ auto isPropertyRequired = [&type, &resolver](const auto &property) {
+ if (!type->isPropertyRequired(property.propertyName()))
+ return false;
+
+ if (type->hasPropertyBindings(property.propertyName()))
+ return false;
+
+ if (property.isAlias()) {
+ QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
+
+ QQmlJSUtils::ResolvedAlias result =
+ QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
+
+ if (result.kind != QQmlJSUtils::AliasTarget_Property)
+ return false;
+
+ // If the top level alias targets a property that is in
+ // the top level scope and that property is required, then
+ // we will already pick up the property during one of the
+ // iterations.
+ // Setting the property or the alias is the same so we
+ // discard one of the two, as otherwise we would require
+ // the user to pass two values for the same property ,in
+ // this case the alias.
+ //
+ // For example in:
+ //
+ // ```
+ // Item {
+ // id: self
+ // required property int foo
+ // property alias bar: self.foo
+ // }
+ // ```
+ //
+ // Both foo and bar are required but setting one or the
+ // other is the same operation so that we should choose
+ // only one.
+ if (result.owner == type &&
+ type->isPropertyRequired(result.property.propertyName()))
+ return false;
+
+ if (result.owner->hasPropertyBindings(result.property.propertyName()))
+ return false;
+ }
+
+ return true;
+ };
+
+ const auto properties = type->properties();
+ std::copy_if(properties.cbegin(), properties.cend(),
+ std::back_inserter(requiredProperties), isPropertyRequired);
+ std::sort(requiredProperties.begin(), requiredProperties.end(),
+ [](const auto &left, const auto &right) {
+ return left.propertyName() < right.propertyName();
+ });
+
+ return requiredProperties;
+}
+
+
+// Populates the internal representation for a
+// RequiredPropertiesBundle, a class that acts as a bundle of initial
+// values that should be set for the required properties of a type.
+static void compileRequiredPropertiesBundle(
+ QmltcType &current,
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+
+ QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
+
+ if (requiredProperties.isEmpty())
+ return;
+
+ current.requiredPropertiesBundle.emplace();
+ current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
+
+ current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
+ std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
+ std::back_inserter(current.requiredPropertiesBundle->members),
+ [](const QQmlJSMetaProperty &property) {
+ QString type = qIsReferenceTypeList(property)
+ ? u"const QList<%1*>&"_s.arg(
+ property.type()->valueType()->internalName())
+ : u"passByConstRefOrValue<%1>"_s.arg(
+ property.type()->augmentedInternalName());
+ return QmltcVariable{ type, property.propertyName() };
+ });
+}
+
+static void compileRootExternalConstructorBody(
+ QmltcType& current,
+ const QQmlJSScope::ConstPtr &type
+) {
+ current.externalCtor.body << u"// document root:"_s;
+ // if it's document root, we want to create our QQmltcObjectCreationBase
+ // that would store all the created objects
+ current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
+ type->internalName());
+ current.externalCtor.body
+ << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
+ current.externalCtor.body << u"creator.set(0, this);"_s; // special case
+
+ QString initializerName = u"initializer"_s;
+ if (current.requiredPropertiesBundle) {
+ // Compose new initializer based on the initial values for required properties.
+ current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
+ for (const auto& member : current.requiredPropertiesBundle->members) {
+ current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
+ QmltcPropertyData(member.name).write, member.name
+ );
+ }
+ current.externalCtor.body << u" initializer(propertyInitializer);"_s;
+ current.externalCtor.body << u"};"_s;
+
+ initializerName = u"newInitializer"_s;
+ }
+
+ // now call init
+ current.externalCtor.body << current.init.name
+ + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
+ u"endInit */ true, %1);"_s.arg(initializerName);
+};
+
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
@@ -87,6 +219,9 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
+ if (inlineComponentAName == inlineComponentBName)
+ return false;
+
// the root comes at last, so (a < b) == true when b is the root and a is not
if (inlineComponentAName && !inlineComponentBName)
return true;
@@ -127,7 +262,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
};
for (const auto &type : pureTypes) {
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
compiledTypes.emplaceBack(); // create empty type
compileType(compiledTypes.back(), type, compile);
}
@@ -142,8 +277,11 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
program.cppPath = m_info.outputCppFile;
program.hPath = m_info.outputHFile;
program.outNamespace = m_info.outputNamespace;
+ program.exportMacro = m_info.exportMacro;
program.compiledTypes = compiledTypes;
program.includes = m_visitor->cppIncludeFiles();
+ if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
+ program.includes += (m_info.exportInclude);
program.urlMethod = urlMethod;
QmltcOutput out;
@@ -186,7 +324,8 @@ void QmltcCompiler::compileType(
// make document root a friend to allow it to access init and endInit
const QString rootInternalName =
m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
- current.otherCode << u"friend class %1;"_s.arg(rootInternalName);
+ if (rootInternalName != current.cppType) // avoid GCC13 warning on self-befriending
+ current.otherCode << "friend class %1;"_L1.arg(rootInternalName);
}
if (documentRoot || inlineComponent) {
auto name = type->inlineComponentName()
@@ -210,8 +349,11 @@ void QmltcCompiler::compileType(
return scope->parentScope();
return scope;
};
- current.otherCode << u"friend class %1;"_s.arg(
- realQmlScope(type->parentScope())->internalName());
+
+ const auto& realScope = realQmlScope(type->parentScope());
+ if (realScope != rootType) {
+ current.otherCode << u"friend class %1;"_s.arg(realScope->internalName());
+ }
}
// make QQmltcObjectCreationHelper a friend of every type since it provides
@@ -240,6 +382,17 @@ void QmltcCompiler::compileType(
current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
+ current.propertyInitializer.name = u"PropertyInitializer"_s;
+ current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public;
+ current.propertyInitializer.constructor.name = current.propertyInitializer.name;
+ current.propertyInitializer.constructor.parameterList = {
+ QmltcVariable(u"%1&"_s.arg(current.cppType), u"component"_s)
+ };
+ current.propertyInitializer.component.cppType = current.cppType + u"&";
+ current.propertyInitializer.component.name = u"component"_s;
+ current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s;
+ current.propertyInitializer.initializedCache.name = u"initializedCache"_s;
+
current.baselineCtor.name = current.cppType;
current.externalCtor.name = current.cppType;
current.init.name = u"QML_init"_s;
@@ -259,19 +412,40 @@ void QmltcCompiler::compileType(
QmltcVariable creator(u"QQmltcObjectCreationHelper*"_s, u"creator"_s);
QmltcVariable engine(u"QQmlEngine*"_s, u"engine"_s);
QmltcVariable parent(u"QObject*"_s, u"parent"_s, u"nullptr"_s);
+ QmltcVariable initializedCache(
+ u"[[maybe_unused]] const QSet<QString>&"_s,
+ u"initializedCache"_s,
+ u"{}"_s
+ );
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
QmltcVariable finalizeFlag(u"bool"_s, u"canFinalize"_s);
current.baselineCtor.parameterList = { parent };
current.endInit.parameterList = { creator, engine };
- current.setComplexBindings.parameterList = { creator, engine };
+ current.setComplexBindings.parameterList = { creator, engine, initializedCache };
current.handleOnCompleted.parameterList = { creator };
if (documentRoot || inlineComponent) {
- current.externalCtor.parameterList = { engine, parent };
- current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
+ const QmltcVariable initializer(
+ u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s.arg(current.propertyInitializer.name),
+ u"initializer"_s,
+ u"[](%1&){}"_s.arg(current.propertyInitializer.name));
+
+ current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
current.beginClass.parameterList = { creator, finalizeFlag };
current.completeComponent.parameterList = { creator, finalizeFlag };
current.finalizeComponent.parameterList = { creator, finalizeFlag };
+
+ compileRequiredPropertiesBundle(current, type, m_typeResolver);
+
+ if (current.requiredPropertiesBundle) {
+ QmltcVariable bundle{
+ u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
+ u"requiredPropertiesBundle"_s,
+ };
+ current.externalCtor.parameterList = { engine, bundle, parent, initializer };
+ } else {
+ current.externalCtor.parameterList = { engine, parent, initializer };
+ }
} else {
current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
@@ -295,18 +469,7 @@ void QmltcCompiler::compileType(
// compilation stub:
current.externalCtor.body << u"Q_UNUSED(engine)"_s;
if (documentRoot || inlineComponent) {
- current.externalCtor.body << u"// document root:"_s;
- // if it's document root, we want to create our QQmltcObjectCreationBase
- // that would store all the created objects
- current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
- type->internalName());
- current.externalCtor.body
- << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
- current.externalCtor.body << u"creator.set(0, this);"_s; // special case
- // now call init
- current.externalCtor.body << current.init.name
- + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
- u"endInit */ true);";
+ compileRootExternalConstructorBody(current, type);
} else {
current.externalCtor.body << u"// not document root:"_s;
// just call init, we don't do any setup here otherwise
@@ -321,7 +484,7 @@ void QmltcCompiler::compileType(
staticCreate.comments
<< u"Used by the engine for singleton creation."_s
<< u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s;
- staticCreate.type = QQmlJSMetaMethod::StaticMethod;
+ staticCreate.type = QQmlJSMetaMethodType::StaticMethod;
staticCreate.access = QQmlJSMetaMethod::Public;
staticCreate.name = u"create"_s;
staticCreate.returnType = u"%1 *"_s.arg(current.cppType);
@@ -354,6 +517,102 @@ static Iterator partitionBindings(Iterator first, Iterator last)
});
}
+// Populates the propertyInitializer of the current type based on the
+// available properties.
+//
+// A propertyInitializer is a generated class that provides a
+// restricted interface that only allows setting property values and
+// internally keep tracks of which properties where actually set,
+// intended to be used to allow the user to set up the initial values
+// when creating an instance of a component.
+//
+// For each property of the current type that is known, is not private
+// and is writable, a setter method is generated.
+// Each setter method knows how to set a specific property, so as to
+// provide a strongly typed interface to property setting, as if the
+// relevant C++ type was used directly.
+//
+// Each setter uses the write method for the proprerty when available
+// and otherwise falls back to a the more generic
+// `QObject::setProperty` for properties where a WRITE method is not
+// available or in scope.
+static void compilePropertyInitializer(QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ static auto isFromExtension = [](const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &scope) {
+ return scope->ownerOfProperty(scope, property.propertyName()).extensionSpecifier != QQmlJSScope::NotExtension;
+ };
+
+ current.propertyInitializer.constructor.initializerList << u"component{component}"_s;
+
+ auto properties = type->properties().values();
+ for (auto& property: properties) {
+ if (property.index() == -1) continue;
+ if (property.isPrivate()) continue;
+ if (!property.isWritable() && !qIsReferenceTypeList(property)) continue;
+
+ const QString name = property.propertyName();
+
+ current.propertyInitializer.propertySetters.emplace_back();
+ auto& compiledSetter = current.propertyInitializer.propertySetters.back();
+
+ compiledSetter.userVisible = true;
+ compiledSetter.returnType = u"void"_s;
+ compiledSetter.name = QmltcPropertyData(property).write;
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(u"QList<%1*>"_s.arg(property.type()->valueType()->internalName())),
+ name + u"_", QString()
+ );
+ } else {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(getUnderlyingType(property)), name + u"_", QString()
+ );
+ }
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s.arg(
+ current.propertyInitializer.component.name, name
+ );
+ compiledSetter.body << u"list_ref_.clear();"_s;
+ compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s.arg(name);
+ compiledSetter.body << u" list_ref_.append(list_item_);"_s;
+ } else if (
+ QQmlJSUtils::bindablePropertyHasDefaultAccessor(property, QQmlJSUtils::PropertyAccessor_Write)
+ ) {
+ compiledSetter.body << u"%1.%2().setValue(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.bindable(), name);
+ } else if (type->hasOwnProperty(name)) {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, QmltcPropertyData(property).write, name);
+ } else if (property.write().isEmpty() || isFromExtension(property, type)) {
+ // We can end here if a WRITE method is not available or
+ // if the method is available but not in this scope, so
+ // that we fallback to the string-based setters..
+ //
+ // For example, types that makes use of QML_EXTENDED
+ // types, will have the extension types properties
+ // available and with a WRITE method, but the WRITE method
+ // will not be available to the extended type, from C++,
+ // as the type does not directly inherit from the
+ // extension type.
+ //
+ // We specifically scope `setProperty` to `QObject` as
+ // certain types might have shadowed the method.
+ // For example, in QtQuick, some types have a property
+ // called `property` with a `setProperty` WRITE method
+ // that will produce the shadowing.
+ compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s.arg(
+ current.propertyInitializer.component.name, name);
+ } else {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.write(), name);
+ }
+
+ compiledSetter.body << u"%1.insert(\"%2\");"_s.arg(
+ current.propertyInitializer.initializedCache.name, name);
+ }
+}
+
void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type)
{
// compile components of a type:
@@ -396,6 +655,7 @@ void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::C
auto bindings = type->ownPropertyBindingsInQmlIROrder();
partitionBindings(bindings.begin(), bindings.end());
+ compilePropertyInitializer(current, type);
compileBinding(current, bindings.begin(), bindings.end(), type, { type });
}
@@ -450,7 +710,7 @@ compileMethodParameters(const QList<QQmlJSMetaParameter> &parameterInfos, bool a
static QString figureReturnType(const QQmlJSMetaMethod &m)
{
const bool isVoidMethod =
- m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal;
+ m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethodType::Signal;
Q_ASSERT(isVoidMethod || m.returnType());
QString type;
if (isVoidMethod) {
@@ -467,10 +727,10 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
const auto returnType = figureReturnType(m);
const QList<QmltcVariable> compiledParams = compileMethodParameters(m.parameters());
- const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
+ const auto methodType = m.methodType();
QStringList code;
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
QmltcCodeGenerator urlGenerator { m_url, m_visitor };
QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
&code, urlGenerator.urlMethodName() + u"()",
@@ -485,7 +745,7 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
compiled.body = std::move(code);
compiled.type = methodType;
compiled.access = m.access();
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
} else {
@@ -1057,7 +1317,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Object);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1115,7 +1375,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s);
if (int id = m_visitor->runtimeId(object); id >= 0) {
- QString idString = m_visitor->addressableScopes().id(object);
+ QString idString = m_visitor->addressableScopes().id(object, object);
if (idString.isEmpty())
idString = u"<unknown>"_s;
QmltcCodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName,
@@ -1154,8 +1414,8 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::ValueSource
- || binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource
+ || binding.bindingType() == QQmlSA::BindingType::Interceptor);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1163,7 +1423,7 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
// NB: object is compiled with compileType(), here just need to use it
QSharedPointer<const QQmlJSScope> object;
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor)
+ if (binding.bindingType() == QQmlSA::BindingType::Interceptor)
object = binding.interceptorType();
else
object = binding.valueSourceType();
@@ -1212,7 +1472,7 @@ void QmltcCompiler::compileAttachedPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1276,7 +1536,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1333,7 +1593,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
auto it = subbindings.begin();
Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) {
- return x.bindingType() != QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() != QQmlSA::BindingType::Script;
}));
compile(it, firstScript);
it = firstScript;
@@ -1354,7 +1614,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
// once the value is written back, process the script bindings
Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) {
- return x.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() == QQmlSA::BindingType::Script;
}));
compile(it, subbindings.end());
}
@@ -1368,8 +1628,8 @@ void QmltcCompiler::compileTranslationBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Translation
- || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation
+ || binding.bindingType() == QQmlSA::BindingType::TranslationById);
const QString &propertyName = binding.propertyName();
@@ -1446,7 +1706,7 @@ void QmltcCompiler::compileBinding(QmltcType &current,
const auto location = binding.sourceLocation();
// make sure group property is not generalized by checking if type really has a property
// called propertyName. If not, it is probably an id.
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty
+ if (binding.bindingType() == QQmlSA::BindingType::GroupProperty
&& type->hasProperty(propertyName)) {
qCWarning(lcQmltcCompiler)
<< QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "
@@ -1493,26 +1753,32 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
accessor.name, constructFromQObject);
};
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::BoolLiteral: {
+ case QQmlSA::BindingType::BoolLiteral: {
const bool value = binding.boolValue();
assignToProperty(metaProperty, value ? u"true"_s : u"false"_s);
break;
}
- case QQmlJSMetaPropertyBinding::NumberLiteral: {
+ case QQmlSA::BindingType::NumberLiteral: {
assignToProperty(metaProperty, QString::number(binding.numberValue()));
break;
}
- case QQmlJSMetaPropertyBinding::StringLiteral: {
- assignToProperty(metaProperty, QQmlJSUtils::toLiteral(binding.stringValue()));
+ case QQmlSA::BindingType::StringLiteral: {
+ QString value = QQmlJSUtils::toLiteral(binding.stringValue());
+ if (auto type = metaProperty.type()) {
+ if (type->internalName() == u"QUrl"_s) {
+ value = u"QUrl(%1)"_s.arg(value);
+ }
+ }
+ assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::RegExpLiteral: {
+ case QQmlSA::BindingType::RegExpLiteral: {
const QString value =
u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue()));
assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::Null: {
+ case QQmlSA::BindingType::Null: {
// poor check: null bindings are only supported for var and objects
Q_ASSERT(propertyType->isSameType(m_typeResolver->varType())
|| propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
@@ -1522,38 +1788,38 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s);
break;
}
- case QQmlJSMetaPropertyBinding::Script: {
+ case QQmlSA::BindingType::Script: {
QString bindingSymbolName = type->internalName() + u'_' + propertyName + u"_binding";
bindingSymbolName.replace(u'.', u'_'); // can happen with group properties
compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType,
accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Object: {
+ case QQmlSA::BindingType::Object: {
compileObjectBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Interceptor:
+ case QQmlSA::BindingType::Interceptor:
Q_FALLTHROUGH();
- case QQmlJSMetaPropertyBinding::ValueSource: {
+ case QQmlSA::BindingType::ValueSource: {
compileValueSourceOrInterceptorBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::AttachedProperty: {
+ case QQmlSA::BindingType::AttachedProperty: {
compileAttachedPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::GroupProperty: {
+ case QQmlSA::BindingType::GroupProperty: {
compileGroupPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::TranslationById:
- case QQmlJSMetaPropertyBinding::Translation: {
+ case QQmlSA::BindingType::TranslationById:
+ case QQmlSA::BindingType::Translation: {
compileTranslationBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Invalid: {
+ case QQmlSA::BindingType::Invalid: {
recordError(binding.sourceLocation(), u"This binding is invalid"_s);
break;
}
@@ -1617,8 +1883,11 @@ static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope
// index is already added as p.index())
if (type->isSameType(owner))
return;
- if (m == QQmlJSScope::ExtensionNamespace) // extension namespace properties are ignored
+
+ // extension namespace and JavaScript properties are ignored
+ if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript)
return;
+
index += int(type->ownProperties().size());
};
QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, owner, increment);
@@ -1649,10 +1918,10 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
Q_ASSERT(!objectClassName_signal.isEmpty());
Q_ASSERT(!objectClassName_slot.isEmpty());
- const auto signalMethods = objectType->methods(name, QQmlJSMetaMethod::Signal);
+ const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal);
Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else
QQmlJSMetaMethod signal = signalMethods.at(0);
- Q_ASSERT(signal.methodType() == QQmlJSMetaMethod::Signal);
+ Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal);
const QString signalName = signal.methodName();
const QString slotName = newSymbol(signalName + u"_slot");
@@ -1672,7 +1941,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
objectType->ownRuntimeFunctionIndex(binding.scriptIndex()),
u"this"_s, // Note: because script bindings always use current QML object scope
signalReturnType, slotParameters);
- slotMethod.type = QQmlJSMetaMethod::Slot;
+ slotMethod.type = QQmlJSMetaMethodType::Slot;
current.functions << std::move(slotMethod);
current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&"
@@ -1681,7 +1950,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
};
switch (binding.scriptKind()) {
- case QQmlJSMetaPropertyBinding::Script_PropertyBinding: {
+ case QQmlSA::ScriptBindingKind::PropertyBinding: {
if (!propertyType) {
recordError(binding.sourceLocation(),
u"Binding on property '" + propertyName + u"' of unknown type");
@@ -1723,19 +1992,20 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
property, valueTypeIndex, accessor.name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_SignalHandler: {
- const auto name = QQmlJSUtils::signalName(propertyName);
+ case QQmlSA::ScriptBindingKind::SignalHandler: {
+ const auto name = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(name.has_value());
compileScriptSignal(*name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_ChangeHandler: {
+ case QQmlSA ::ScriptBindingKind::ChangeHandler: {
const QString objectClassName = objectType->internalName();
const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor");
- const auto signalName = QQmlJSUtils::signalName(propertyName);
+ const auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(signalName.has_value()); // an error somewhere else
- const auto actualProperty = QQmlJSUtils::changeHandlerProperty(objectType, *signalName);
+ const auto actualProperty =
+ QQmlJSUtils::propertyFromChangedHandler(objectType, propertyName);
Q_ASSERT(actualProperty.has_value()); // an error somewhere else
const auto actualPropertyType = actualProperty->type();
if (!actualPropertyType) {
@@ -1759,9 +2029,12 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
current.children << compileScriptBindingPropertyChangeHandler(
binding, objectType, m_urlMethodName, bindingFunctorName, objectClassName);
+ current.setComplexBindings.body << u"if (!%1.contains(\"%2\"))"_s.arg(
+ current.propertyInitializer.initializedCache.name, propertyName);
+
// TODO: this could be dropped if QQmlEngine::setContextForObject() is
// done before currently generated C++ object is constructed
- current.setComplexBindings.body << bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ current.setComplexBindings.body << u" "_s + bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ bindingFunctorName + u">("
+ QmltcCodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
+ u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index d5f07aa3b0..3deab6d44e 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -25,6 +25,8 @@ struct QmltcCompilerInfo
QString outputHFile;
QString outputNamespace;
QString resourcePath;
+ QString exportMacro;
+ QString exportInclude;
};
class QmltcCompiler
@@ -48,7 +50,7 @@ public:
static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding)
{
// TODO: translation bindings (once supported) are also complex?
- return binding.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ return binding.bindingType() == QQmlSA::BindingType::Script;
}
private:
@@ -185,14 +187,14 @@ private:
bool hasErrors() const { return m_logger->hasErrors(); }
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
- LoggerWarningId id = qmlCompiler)
+ QQmlJS::LoggerWarningId id = qmlCompiler)
{
// pretty much any compiler error is a critical error (we cannot
// generate code - compilation fails)
m_logger->log(message, id, location);
}
void recordError(const QV4::CompiledData::Location &location, const QString &message,
- LoggerWarningId id = qmlCompiler)
+ QQmlJS::LoggerWarningId id = qmlCompiler)
{
recordError(QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message,
id);
diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp
index 601cf1bbed..cd1735bc07 100644
--- a/tools/qmltc/qmltccompilerpieces.cpp
+++ b/tools/qmltc/qmltccompilerpieces.cpp
@@ -15,8 +15,8 @@ static QString scopeName(const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isFullyResolved());
const auto scopeType = scope->scopeType();
- if (scopeType == QQmlJSScope::GroupedPropertyScope
- || scopeType == QQmlJSScope::AttachedPropertyScope) {
+ if (scopeType == QQmlSA::ScopeType::GroupedPropertyScope
+ || scopeType == QQmlSA::ScopeType::AttachedPropertyScope) {
return scope->baseType()->internalName();
}
return scope->internalName();
@@ -237,15 +237,18 @@ void QmltcCodeGenerator::generate_createBindingOnProperty(
}
*block += prologue;
- *block << value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
+ *block << u" "_s + value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
*block += epilogue;
} else {
QString createBindingForNonBindable =
- u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ u" "_s
+ + u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ u", " + scope + u", " + QString::number(functionIndex) + u", " + target + u", "
+ QString::number(propertyIndex) + u", " + QString::number(valueTypeIndex) + u", "
+ propName + u")";
// Note: in this version, the binding is set implicitly
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
*block << createBindingForNonBindable + u";";
}
}
@@ -293,8 +296,8 @@ void QmltcCodeGenerator::generate_createTranslationBindingOnProperty(
QmltcCodeGenerator::PreparedValue
QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value)
{
- auto isDerivedFromBuiltin = [](QQmlJSScope::ConstPtr t, const QString &builtin) {
- for (; t; t = t->baseType()) {
+ auto isDerivedFromBuiltin = [](const QQmlJSScope::ConstPtr &derived, const QString &builtin) {
+ for (QQmlJSScope::ConstPtr t = derived; t; t = t->baseType()) {
if (t->internalName() == builtin)
return true;
}
@@ -302,7 +305,7 @@ QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p,
};
QStringList prologue;
QStringList epilogue;
- auto propType = p.type();
+ const QQmlJSScope::ConstPtr propType = p.type();
if (isDerivedFromBuiltin(propType, u"QVariant"_s)) {
const QString variantName = u"var_" + p.propertyName();
prologue << u"{ // accepts QVariant"_s;
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index bfd22e1b79..3252f19e86 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -152,14 +152,17 @@ struct QmltcCodeGenerator
/*!
\internal
- Generates \a{current.init}'s code. The init method sets up a QQmlContext for
- the object and (in case \a type is a document root) calls other object
- creation methods in a well-defined order:
+ Generates \a{current.init}'s code. The init method sets up a
+ QQmlContext for the object and (in case \a type is a document
+ root) calls other object creation methods, and a user-provided
+ initialization callback, in a well-defined order:
1. current.beginClass
2. current.endInit
- 3. current.completeComponent
- 4. current.finalizeComponent
- 5. current.handleOnCompleted
+ 3. user-provided initialization function
+ 4. current.setComplexBindings
+ 5. current.completeComponent
+ 6. current.finalizeComponent
+ 7. current.handleOnCompleted
This function returns a QScopeGuard with the final instructions that have to
be generated at a later point, once everything else is compiled.
@@ -260,7 +263,7 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
if (int id = visitor->runtimeId(type); id >= 0) {
current.init.body << u"// 3. set id since it is provided"_s;
- QString idString = visitor->addressableScopes().id(type);
+ QString idString = visitor->addressableScopes().id(type, type);
if (idString.isEmpty())
idString = u"<unknown>"_s;
QmltcCodeGenerator::generate_setIdValue(&current.init.body, u"context"_s, id, u"this"_s,
@@ -294,8 +297,14 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
.arg(current.beginClass.name);
current.init.body << QStringLiteral(" %1(creator, engine);")
.arg(current.endInit.name);
- current.init.body << QStringLiteral(" %1(creator, engine);")
- .arg(current.setComplexBindings.name);
+
+ current.init.body << QStringLiteral(" {");
+ current.init.body << QStringLiteral(" PropertyInitializer propertyInitializer(*this);");
+ current.init.body << QStringLiteral(" initializer(propertyInitializer);");
+ current.init.body << QStringLiteral(" %1(creator, engine, propertyInitializer.initializedCache);").arg(current.setComplexBindings.name);
+ current.init.body << QStringLiteral(" }");
+
+
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
.arg(current.completeComponent.name);
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 8884ddc3a3..de531f718d 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -75,7 +75,7 @@ struct QmltcMethodBase
struct QmltcMethod : QmltcMethodBase
{
QString returnType; // C++ return type
- QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
+ QQmlJSMetaMethodType type = QQmlJSMetaMethodType::Method; // Qt function type
// TODO: should be a better way to handle this
bool userVisible = false; // tells if a function is prioritized during the output generation
@@ -92,6 +92,42 @@ struct QmltcDtor : QmltcMethodBase
{
};
+// Represents a generated class that knows how to set the public,
+// writable properties of a compiled QML -> C++ type.
+// This is generally intended to be available for the root of the
+// document to allow the user to set the initial values for
+// properties, when creating a component, with support for strong
+// typing.
+struct QmltcPropertyInitializer {
+ QString name;
+
+ QmltcCtor constructor;
+
+ // A member containing a reference to the object for which the
+ // properties should be set.
+ QmltcVariable component;
+
+ // A member containing a cache of properties that were actually
+ // set that can be referenced later..
+ QmltcVariable initializedCache;
+
+ // Setter methods for each property.
+ QList<QmltcMethod> propertySetters;
+};
+
+// Represents a generated class that contains a bundle of values to
+// initialize the required properties of a type.
+//
+// This is generally intended to be available for the root component
+// of the document, where it will be used as a constructor argument to
+// force the user to provide initial values for the required
+// properties of the constructed type.
+struct QmltcRequiredPropertiesBundle {
+ QString name;
+
+ QList<QmltcVariable> members;
+};
+
// Represents QML -> C++ compiled type
struct QmltcType
{
@@ -131,6 +167,12 @@ struct QmltcType
// needed for singletons
std::optional<QmltcMethod> staticCreate{};
+
+ // A proxy class that provides a restricted interface that only
+ // allows setting the properties of the type.
+ QmltcPropertyInitializer propertyInitializer{};
+
+ std::optional<QmltcRequiredPropertiesBundle> requiredPropertiesBundle{};
};
// Represents whole QML program, compiled to C++
@@ -140,6 +182,8 @@ struct QmltcProgram
QString cppPath; // C++ output .cpp path
QString hPath; // C++ output .h path
QString outNamespace;
+ QString exportMacro; // if not empty, the macro that should be used to export the generated
+ // classes
QSet<QString> includes; // non-default C++ include files
QmltcMethod urlMethod; // returns QUrl of the QML document
diff --git a/tools/qmltc/qmltcpropertyutils.h b/tools/qmltc/qmltcpropertyutils.h
index db26c498dd..8a69e5ef09 100644
--- a/tools/qmltc/qmltcpropertyutils.h
+++ b/tools/qmltc/qmltcpropertyutils.h
@@ -6,6 +6,7 @@
#include <private/qqmljsmetatypes_p.h>
#include <private/qqmljsscope_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
QT_BEGIN_NAMESPACE
@@ -35,13 +36,11 @@ struct QmltcPropertyData
QmltcPropertyData(const QString &propertyName)
{
- const QString nameWithUppercase = propertyName[0].toUpper() + propertyName.sliced(1);
-
read = propertyName;
- write = u"set" + nameWithUppercase;
- bindable = u"bindable" + nameWithUppercase;
- notify = propertyName + u"Changed";
- reset = u"reset" + nameWithUppercase;
+ write = QQmlSignalNames::addPrefixToPropertyName(u"set", propertyName);
+ bindable = QQmlSignalNames::addPrefixToPropertyName(u"bindable", propertyName);
+ notify = QQmlSignalNames::propertyNameToChangedSignalName(propertyName);
+ reset = QQmlSignalNames::addPrefixToPropertyName(u"reset", propertyName);
}
QString read;
diff --git a/tools/qmltc/qmltctyperesolver.cpp b/tools/qmltc/qmltctyperesolver.cpp
index 045f3af088..9c53686736 100644
--- a/tools/qmltc/qmltctyperesolver.cpp
+++ b/tools/qmltc/qmltctyperesolver.cpp
@@ -12,15 +12,12 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qdiriterator.h>
-Q_LOGGING_CATEGORY(lcTypeResolver2, "qml.qmltc.typeresolver", QtInfoMsg);
+Q_STATIC_LOGGING_CATEGORY(lcTypeResolver2, "qml.qmltc.typeresolver", QtInfoMsg);
void QmltcTypeResolver::init(QmltcVisitor *visitor, QQmlJS::AST::Node *program)
{
QQmlJSTypeResolver::init(visitor, program);
- QQmlJSLiteralBindingCheck literalCheck;
- literalCheck.run(visitor, this);
-
m_root = visitor->result();
QQueue<QQmlJSScope::Ptr> objects;
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 236ad76467..a6ec1f8661 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -8,6 +8,7 @@
#include <QtCore/qstack.h>
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qqmljsutils_p.h>
@@ -101,6 +102,9 @@ void QmltcVisitor::findCppIncludes()
// look in type
addCppInclude(type);
+ if (type->isListProperty())
+ addCppInclude(type->valueType());
+
// look in type's base type
auto base = type->baseType();
if (!base && type->isComposite())
@@ -133,8 +137,9 @@ void QmltcVisitor::findCppIncludes()
Q_ASSERT(type);
const auto scopeType = type->scopeType();
- if (scopeType != QQmlJSScope::QMLScope && scopeType != QQmlJSScope::GroupedPropertyScope
- && scopeType != QQmlJSScope::AttachedPropertyScope) {
+ if (scopeType != QQmlSA::ScopeType::QMLScope
+ && scopeType != QQmlSA::ScopeType::GroupedPropertyScope
+ && scopeType != QQmlSA::ScopeType::AttachedPropertyScope) {
continue;
}
@@ -174,7 +179,7 @@ void QmltcVisitor::findCppIncludes()
static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
{
- Q_ASSERT(scope->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope);
Q_ASSERT(!scope->isArrayScope());
Q_ASSERT(!scope->baseTypeName().isEmpty());
// the scope is guaranteed to be a new QML type, so any prefixes (separated
@@ -197,7 +202,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
}
// we're not interested in non-QML scopes
- if (m_currentScope->scopeType() != QQmlJSScope::QMLScope)
+ if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope)
return true;
if (m_currentScope->isInlineComponent()) {
@@ -216,7 +221,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object)
{
- if (m_currentScope->scopeType() == QQmlJSScope::QMLScope)
+ if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope)
m_qmlTypeNames.removeLast();
QQmlJSImportVisitor::endVisit(object);
}
@@ -268,7 +273,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
owner->addOwnProperty(property);
}
- const QString notifyName = name + u"Changed"_s;
+ const QString notifyName = QQmlSignalNames::propertyNameToChangedSignalName(name);
// also check that notify is already a method of the scope
{
auto owningScope = m_savedBindingOuterScope ? m_savedBindingOuterScope : m_currentScope;
@@ -280,7 +285,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
u"internal error: %1 found for property '%2'"_s.arg(errorString, name),
qmlCompiler, publicMember->identifierToken);
return false;
- } else if (methods[0].methodType() != QQmlJSMetaMethod::Signal) {
+ } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) {
m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg(
notifyName, name),
qmlCompiler, publicMember->identifierToken);
@@ -336,17 +341,17 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
for (const QList<QQmlJSScope::ConstPtr> &qmlTypes : m_pureQmlTypes)
for (const QQmlJSScope::ConstPtr &type : qmlTypes)
- checkForNamingCollisionsWithCpp(type);
+ checkNamesAndTypes(type);
}
QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
{
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::Object:
+ case QQmlSA::BindingType::Object:
return binding.objectType();
- case QQmlJSMetaPropertyBinding::Interceptor:
+ case QQmlSA::BindingType::Interceptor:
return binding.interceptorType();
- case QQmlJSMetaPropertyBinding::ValueSource:
+ case QQmlSA::BindingType::ValueSource:
return binding.valueSourceType();
// TODO: AttachedProperty and GroupProperty are not supported yet,
// but have to also be acknowledged here
@@ -435,7 +440,7 @@ void QmltcVisitor::postVisitResolve(
// match scopes to indices of QmlIR::Object from QmlIR::Document
qsizetype count = 0;
const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
- if (current->scopeType() != QQmlJSScope::QMLScope || current->isArrayScope())
+ if (current->scopeType() != QQmlSA::ScopeType::QMLScope || current->isArrayScope())
return;
Q_ASSERT(!m_qmlIrObjectIndices.contains(current));
m_qmlIrObjectIndices[current] = count;
@@ -547,9 +552,9 @@ void QmltcVisitor::postVisitResolve(
if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
return;
switch (scope->scopeType()) {
- case QQmlJSScope::QMLScope:
- case QQmlJSScope::GroupedPropertyScope:
- case QQmlJSScope::AttachedPropertyScope: {
+ case QQmlSA::ScopeType::QMLScope:
+ case QQmlSA::ScopeType::GroupedPropertyScope:
+ case QQmlSA::ScopeType::AttachedPropertyScope: {
++qmlScopeCount[scope->enclosingInlineComponentName()];
break;
}
@@ -653,7 +658,7 @@ void QmltcVisitor::setupAliases()
}
}
-void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type)
+void QmltcVisitor::checkNamesAndTypes(const QQmlJSScope::ConstPtr &type)
{
static const QString cppKeywords[] = {
u"alignas"_s,
@@ -672,20 +677,21 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
u"case"_s,
u"catch"_s,
u"char"_s,
- u"char8_t"_s,
u"char16_t"_s,
u"char32_t"_s,
+ u"char8_t"_s,
u"class"_s,
+ u"co_await"_s,
+ u"co_return"_s,
+ u"co_yield"_s,
u"compl"_s,
u"concept"_s,
u"const"_s,
+ u"const_cast"_s,
u"consteval"_s,
u"constexpr"_s,
- u"const_cast"_s,
+ u"constinit"_s,
u"continue"_s,
- u"co_await"_s,
- u"co_return"_s,
- u"co_yield"_s,
u"decltype"_s,
u"default"_s,
u"delete"_s,
@@ -753,6 +759,7 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
u"xor"_s,
u"xor_eq"_s,
};
+ Q_ASSERT(std::is_sorted(std::begin(cppKeywords), std::end(cppKeywords)));
const auto isReserved = [&](QStringView word) {
if (word.startsWith(QChar(u'_')) && word.size() >= 2
@@ -769,6 +776,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
qmlCompiler, type->sourceLocation());
};
+ const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
+ QStringView name, QStringView errorPrefix) {
+ if (type->moduleName().isEmpty() || typeToCheck.isNull())
+ return;
+
+ if (typeToCheck->isComposite() && typeToCheck->moduleName() != type->moduleName()) {
+ m_logger->log(
+ QStringLiteral(
+ "Can't compile the %1 type \"%2\" to C++ because it "
+ "lives in \"%3\" instead of the current file's \"%4\" QML module.")
+ .arg(errorPrefix, name, typeToCheck->moduleName(), type->moduleName()),
+ qmlCompiler, type->sourceLocation());
+ }
+ };
+
+ validateType(type->baseType(), type->baseTypeName(), u"QML base");
+
const auto enums = type->ownEnumerations();
for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
const QQmlJSMetaEnum e = it.value();
@@ -783,16 +807,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
const QQmlJSMetaProperty &p = it.value();
validate(p.propertyName(), u"Property");
+
+ if (!p.isAlias() && !p.typeName().isEmpty())
+ validateType(p.type(), p.typeName(), u"QML property");
}
const auto methods = type->ownMethods();
for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
const QQmlJSMetaMethod &m = it.value();
validate(m.methodName(), u"Method");
+ if (!m.returnTypeName().isEmpty())
+ validateType(m.returnType(), m.returnTypeName(), u"QML method return");
- const auto parameterNames = m.parameterNames();
- for (const auto &name : parameterNames)
- validate(name, u"Method '%1' parameter"_s.arg(m.methodName()));
+ for (const auto &parameter : m.parameters()) {
+ validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
+ if (!parameter.typeName().isEmpty())
+ validateType(parameter.type(), parameter.typeName(), u"QML parameter");
+ }
}
// TODO: one could also test signal handlers' parameters but we do not store
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
index 0ec9349527..111df0e885 100644
--- a/tools/qmltc/qmltcvisitor.h
+++ b/tools/qmltc/qmltcvisitor.h
@@ -22,7 +22,7 @@ class QmltcVisitor : public QQmlJSImportVisitor
void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>>
&qmlIrOrderedBindings);
void setupAliases();
- void checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type);
+ void checkNamesAndTypes(const QQmlJSScope::ConstPtr &type);
void setRootFilePath();
QString sourceDirectoryPath(const QString &path);
@@ -57,7 +57,7 @@ public:
qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
return m_creationIndices.value(type, -1);
}
@@ -68,13 +68,13 @@ public:
qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
return m_syntheticTypeIndices.value(type, -1);
}
qsizetype qmlIrObjectIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
Q_ASSERT(m_qmlIrObjectIndices.contains(type));
return m_qmlIrObjectIndices.value(type, -1);
}