2021-09-16 07:35:33 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
**
|
|
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qmltccompiler.h"
|
2021-09-17 15:01:56 +00:00
|
|
|
#include "qmltcoutputir.h"
|
|
|
|
#include "qmltccodewriter.h"
|
2021-10-29 09:56:25 +00:00
|
|
|
#include "qmltcpropertyutils.h"
|
2021-11-12 08:28:33 +00:00
|
|
|
#include "qmltccompilerpieces.h"
|
2021-11-01 15:18:30 +00:00
|
|
|
|
2022-03-10 08:37:01 +00:00
|
|
|
#include "prototype/codegenerator.h"
|
|
|
|
|
2021-11-15 13:31:42 +00:00
|
|
|
#include <QtCore/qloggingcategory.h>
|
2021-12-17 14:52:17 +00:00
|
|
|
#include <private/qqmljsutils_p.h>
|
2021-09-16 07:35:33 +00:00
|
|
|
|
2021-10-29 10:43:44 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2021-09-16 07:35:33 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
|
|
|
|
const QString QmltcCodeGenerator::typeCountName = u"q_qmltc_typeCount"_s;
|
2021-11-12 08:28:33 +00:00
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
|
|
|
|
QQmlJSLogger *logger)
|
|
|
|
: m_url(url), m_typeResolver(resolver), m_visitor(visitor), m_logger(logger)
|
2021-09-16 07:35:33 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(m_typeResolver);
|
2021-09-21 08:52:24 +00:00
|
|
|
Q_ASSERT(!hasErrors());
|
2021-09-16 07:35:33 +00:00
|
|
|
}
|
|
|
|
|
2022-03-10 08:37:01 +00:00
|
|
|
// needed due to std::unique_ptr<CodeGenerator> with CodeGenerator being
|
|
|
|
// incomplete type in the header (~std::unique_ptr<> fails with a static_assert)
|
|
|
|
QmltcCompiler::~QmltcCompiler() = default;
|
|
|
|
|
|
|
|
void QmltcCompiler::compile(const QmltcCompilerInfo &info, QmlIR::Document *doc)
|
2021-09-16 07:35:33 +00:00
|
|
|
{
|
|
|
|
m_info = info;
|
2021-09-21 08:52:24 +00:00
|
|
|
Q_ASSERT(!m_info.outputCppFile.isEmpty());
|
|
|
|
Q_ASSERT(!m_info.outputHFile.isEmpty());
|
|
|
|
Q_ASSERT(!m_info.resourcePath.isEmpty());
|
|
|
|
|
2022-03-10 08:37:01 +00:00
|
|
|
m_prototypeCodegen =
|
2021-11-19 12:04:05 +00:00
|
|
|
std::make_unique<CodeGenerator>(m_url, m_logger, doc, m_typeResolver, m_visitor, &info);
|
2022-03-10 08:37:01 +00:00
|
|
|
|
|
|
|
QSet<QString> cppIncludesFromPrototype;
|
|
|
|
m_prototypeCodegen->prepare(&cppIncludesFromPrototype);
|
|
|
|
if (hasErrors())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto isComponent = [](const QQmlJSScope::ConstPtr &type) {
|
|
|
|
auto base = type->baseType();
|
2022-03-21 09:21:18 +00:00
|
|
|
return base && base->internalName() == u"QQmlComponent"_s;
|
2022-03-10 08:37:01 +00:00
|
|
|
};
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
// 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());
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2022-04-26 08:42:16 +00:00
|
|
|
QmltcCodeGenerator generator { m_url, m_visitor };
|
|
|
|
|
2021-11-12 08:28:33 +00:00
|
|
|
QmltcMethod urlMethod;
|
2022-04-26 08:42:16 +00:00
|
|
|
compileUrlMethod(urlMethod, generator.urlMethodName());
|
2022-03-10 08:37:01 +00:00
|
|
|
m_prototypeCodegen->setUrlMethodName(urlMethod.name);
|
|
|
|
|
|
|
|
const auto objects = m_prototypeCodegen->objects();
|
|
|
|
QList<CodeGenerator::CodeGenObject> filteredObjects;
|
|
|
|
filteredObjects.reserve(objects.size());
|
|
|
|
std::copy_if(objects.cbegin(), objects.cend(), std::back_inserter(filteredObjects),
|
|
|
|
[&](const auto &object) { return types.contains(object.type); });
|
|
|
|
|
|
|
|
QList<QmltcType> compiledTypes;
|
|
|
|
QQmlJSScope::ConstPtr root = m_visitor->result();
|
|
|
|
if (isComponent(root)) {
|
|
|
|
compiledTypes.reserve(1);
|
|
|
|
compiledTypes.emplaceBack(); // create empty type
|
2022-04-26 08:42:16 +00:00
|
|
|
const auto compile = [&](QmltcType ¤t, const QQmlJSScope::ConstPtr &type) {
|
|
|
|
generator.generate_initCodeForTopLevelComponent(current, type);
|
2022-03-10 08:37:01 +00:00
|
|
|
};
|
|
|
|
Q_ASSERT(root == filteredObjects.at(0).type);
|
2021-11-19 12:04:05 +00:00
|
|
|
compileType(compiledTypes.back(), root, compile);
|
2022-03-10 08:37:01 +00:00
|
|
|
} else {
|
2021-11-19 12:04:05 +00:00
|
|
|
const auto compile = [this](QmltcType ¤t, const QQmlJSScope::ConstPtr &type) {
|
|
|
|
compileTypeElements(current, type);
|
2022-03-10 08:37:01 +00:00
|
|
|
};
|
2021-11-12 08:28:33 +00:00
|
|
|
|
2022-03-10 08:37:01 +00:00
|
|
|
compiledTypes.reserve(filteredObjects.size());
|
|
|
|
for (const auto &object : filteredObjects) {
|
|
|
|
Q_ASSERT(object.type->scopeType() == QQmlJSScope::QMLScope);
|
|
|
|
if (m_prototypeCodegen->ignoreObject(object))
|
|
|
|
continue;
|
|
|
|
compiledTypes.emplaceBack(); // create empty type
|
2021-11-19 12:04:05 +00:00
|
|
|
compileType(compiledTypes.back(), object.type, compile);
|
2022-03-10 08:37:01 +00:00
|
|
|
}
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
2022-03-10 08:37:01 +00:00
|
|
|
if (hasErrors())
|
|
|
|
return;
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
QmltcProgram program;
|
|
|
|
program.url = m_url;
|
|
|
|
program.cppPath = m_info.outputCppFile;
|
|
|
|
program.hPath = m_info.outputHFile;
|
2021-10-20 09:14:52 +00:00
|
|
|
program.outNamespace = m_info.outputNamespace;
|
2021-09-21 08:52:24 +00:00
|
|
|
program.compiledTypes = compiledTypes;
|
2022-03-10 08:37:01 +00:00
|
|
|
program.includes = m_visitor->cppIncludeFiles() | cppIncludesFromPrototype;
|
2021-11-12 08:28:33 +00:00
|
|
|
program.urlMethod = urlMethod;
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
QmltcOutput out;
|
|
|
|
QmltcOutputWrapper code(out);
|
|
|
|
QmltcCodeWriter::write(code, program);
|
2021-09-16 07:35:33 +00:00
|
|
|
}
|
|
|
|
|
2022-04-26 08:42:16 +00:00
|
|
|
void QmltcCompiler::compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName)
|
2021-11-12 08:28:33 +00:00
|
|
|
{
|
2022-04-26 08:42:16 +00:00
|
|
|
urlMethod.name = urlMethodName;
|
2022-03-21 09:21:18 +00:00
|
|
|
urlMethod.returnType = u"const QUrl&"_s;
|
|
|
|
urlMethod.body << u"static QUrl url {QStringLiteral(\"qrc:%1\")};"_s.arg(m_info.resourcePath);
|
|
|
|
urlMethod.body << u"return url;"_s;
|
|
|
|
urlMethod.declarationPrefixes << u"static"_s;
|
|
|
|
urlMethod.modifiers << u"noexcept"_s;
|
2021-11-12 08:28:33 +00:00
|
|
|
}
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
void QmltcCompiler::compileType(
|
|
|
|
QmltcType ¤t, const QQmlJSScope::ConstPtr &type,
|
|
|
|
std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements)
|
2021-09-21 08:52:24 +00:00
|
|
|
{
|
|
|
|
if (type->isSingleton()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
recordError(type->sourceLocation(), u"Singleton types are not supported"_s);
|
2021-09-21 08:52:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-28 12:18:04 +00:00
|
|
|
Q_ASSERT(!type->internalName().isEmpty());
|
2021-09-21 08:52:24 +00:00
|
|
|
current.cppType = type->internalName();
|
2021-10-28 12:18:04 +00:00
|
|
|
Q_ASSERT(!type->baseType()->internalName().isEmpty());
|
2021-09-21 08:52:24 +00:00
|
|
|
const QString baseClass = type->baseType()->internalName();
|
|
|
|
|
2021-10-22 10:02:37 +00:00
|
|
|
const auto rootType = m_visitor->result();
|
|
|
|
const bool documentRoot = (type == rootType);
|
2021-11-19 12:04:05 +00:00
|
|
|
const bool isAnonymous = !documentRoot || type->internalName().at(0).isLower();
|
|
|
|
|
2022-04-26 08:42:16 +00:00
|
|
|
QmltcCodeGenerator generator { m_url, m_visitor };
|
2021-09-21 08:52:24 +00:00
|
|
|
|
|
|
|
current.baseClasses = { baseClass };
|
|
|
|
if (!documentRoot) {
|
2022-03-03 13:03:58 +00:00
|
|
|
// make document root a friend to allow it to access init and endInit
|
2022-03-21 09:21:18 +00:00
|
|
|
current.otherCode << u"friend class %1;"_s.arg(rootType->internalName());
|
2021-11-19 12:04:05 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
};
|
2022-03-21 09:21:18 +00:00
|
|
|
current.otherCode << u"friend class %1;"_s.arg(
|
2021-11-19 12:04:05 +00:00
|
|
|
realQmlScope(type->parentScope())->internalName());
|
2021-09-21 08:52:24 +00:00
|
|
|
} else {
|
2021-10-22 10:02:37 +00:00
|
|
|
// make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
|
|
|
|
// be created for the root object
|
2022-03-21 09:21:18 +00:00
|
|
|
current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s.arg(
|
2021-10-22 10:02:37 +00:00
|
|
|
rootType->internalName());
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
QmltcMethod typeCountMethod;
|
|
|
|
typeCountMethod.name = QmltcCodeGenerator::typeCountName;
|
2022-03-21 09:21:18 +00:00
|
|
|
typeCountMethod.returnType = u"uint"_s;
|
2021-11-19 12:04:05 +00:00
|
|
|
typeCountMethod.body << u"return " + generator.generate_typeCount() + u";";
|
|
|
|
current.typeCount = typeCountMethod;
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
2021-11-19 12:04:05 +00:00
|
|
|
// make QQmltcObjectCreationHelper a friend of every type since it provides
|
|
|
|
// useful helper methods for all types
|
2022-03-21 09:21:18 +00:00
|
|
|
current.otherCode << u"friend class QT_PREPEND_NAMESPACE(QQmltcObjectCreationHelper);"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2021-10-28 12:18:04 +00:00
|
|
|
current.mocCode = {
|
2022-03-21 09:21:18 +00:00
|
|
|
u"Q_OBJECT"_s,
|
2021-10-28 12:18:04 +00:00
|
|
|
// Note: isAnonymous holds for non-root types in the document as well
|
2022-03-21 09:21:18 +00:00
|
|
|
isAnonymous ? u"QML_ANONYMOUS"_s : u"QML_ELEMENT"_s,
|
2021-10-28 12:18:04 +00:00
|
|
|
};
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2021-10-22 10:02:37 +00:00
|
|
|
// add special member functions
|
2022-03-03 13:03:58 +00:00
|
|
|
current.baselineCtor.access = QQmlJSMetaMethod::Protected;
|
2021-11-19 12:04:05 +00:00
|
|
|
if (documentRoot) {
|
|
|
|
current.externalCtor.access = QQmlJSMetaMethod::Public;
|
|
|
|
} else {
|
|
|
|
current.externalCtor.access = QQmlJSMetaMethod::Protected;
|
|
|
|
}
|
2021-10-22 10:02:37 +00:00
|
|
|
current.init.access = QQmlJSMetaMethod::Protected;
|
2021-11-19 12:04:05 +00:00
|
|
|
current.beginClass.access = QQmlJSMetaMethod::Protected;
|
2022-03-03 13:03:58 +00:00
|
|
|
current.endInit.access = QQmlJSMetaMethod::Protected;
|
2021-11-19 12:04:05 +00:00
|
|
|
current.completeComponent.access = QQmlJSMetaMethod::Protected;
|
|
|
|
current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
|
|
|
|
current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
|
2021-10-22 10:02:37 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
current.baselineCtor.name = current.cppType;
|
|
|
|
current.externalCtor.name = current.cppType;
|
2022-03-21 09:21:18 +00:00
|
|
|
current.init.name = u"QML_init"_s;
|
|
|
|
current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_s;
|
|
|
|
current.beginClass.name = u"QML_beginClass"_s;
|
|
|
|
current.beginClass.returnType = u"void"_s;
|
|
|
|
current.endInit.name = u"QML_endInit"_s;
|
|
|
|
current.endInit.returnType = u"void"_s;
|
|
|
|
current.completeComponent.name = u"QML_completeComponent"_s;
|
|
|
|
current.completeComponent.returnType = u"void"_s;
|
|
|
|
current.finalizeComponent.name = u"QML_finalizeComponent"_s;
|
|
|
|
current.finalizeComponent.returnType = u"void"_s;
|
|
|
|
current.handleOnCompleted.name = u"QML_handleOnCompleted"_s;
|
|
|
|
current.handleOnCompleted.returnType = u"void"_s;
|
|
|
|
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 ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
|
|
|
|
QmltcVariable finalizeFlag(u"bool"_s, u"canFinalize"_s);
|
2021-11-19 12:04:05 +00:00
|
|
|
current.baselineCtor.parameterList = { parent };
|
2021-09-21 08:52:24 +00:00
|
|
|
if (documentRoot) {
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.parameterList = { engine, parent };
|
2021-10-22 10:02:37 +00:00
|
|
|
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
|
2021-11-19 12:04:05 +00:00
|
|
|
current.beginClass.parameterList = { creator, finalizeFlag };
|
2022-03-03 13:03:58 +00:00
|
|
|
current.endInit.parameterList = { creator, engine, finalizeFlag };
|
2021-11-19 12:04:05 +00:00
|
|
|
current.completeComponent.parameterList = { creator, finalizeFlag };
|
|
|
|
current.finalizeComponent.parameterList = { creator, finalizeFlag };
|
|
|
|
current.handleOnCompleted.parameterList = { creator, finalizeFlag };
|
2021-09-21 08:52:24 +00:00
|
|
|
} else {
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.parameterList = { creator, engine, parent };
|
2021-10-22 10:02:37 +00:00
|
|
|
current.init.parameterList = { creator, engine, ctxtdata };
|
2021-11-19 12:04:05 +00:00
|
|
|
current.beginClass.parameterList = { creator };
|
2022-03-03 13:03:58 +00:00
|
|
|
current.endInit.parameterList = { creator, engine };
|
2021-11-19 12:04:05 +00:00
|
|
|
current.completeComponent.parameterList = { creator };
|
|
|
|
current.finalizeComponent.parameterList = { creator };
|
|
|
|
current.handleOnCompleted.parameterList = { creator };
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.initializerList = { current.baselineCtor.name + u"(" + parent.name
|
|
|
|
+ u")" };
|
2022-04-28 09:07:30 +00:00
|
|
|
if (QQmlJSUtils::hasCompositeBase(type)) {
|
2021-09-21 08:52:24 +00:00
|
|
|
// call parent's (QML type's) basic ctor from this. that one will take
|
|
|
|
// care about QObject::setParent()
|
2022-03-03 13:03:58 +00:00
|
|
|
current.baselineCtor.initializerList = { baseClass + u"(" + parent.name + u")" };
|
2021-09-21 08:52:24 +00:00
|
|
|
} else {
|
|
|
|
// default call to ctor is enough, but QQml_setParent_noEvent() is
|
|
|
|
// needed (note: faster? version of QObject::setParent())
|
2022-03-03 13:03:58 +00:00
|
|
|
current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// compilation stub:
|
2022-03-21 09:21:18 +00:00
|
|
|
current.externalCtor.body << u"Q_UNUSED(engine);"_s;
|
|
|
|
current.endInit.body << u"Q_UNUSED(engine);"_s;
|
|
|
|
current.endInit.body << u"Q_UNUSED(creator);"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
if (documentRoot) {
|
2022-03-21 09:21:18 +00:00
|
|
|
current.externalCtor.body << u"// document root:"_s;
|
2021-10-22 10:02:37 +00:00
|
|
|
// if it's document root, we want to create our QQmltcObjectCreationBase
|
|
|
|
// that would store all the created objects
|
2022-03-21 09:21:18 +00:00
|
|
|
current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
|
2021-10-22 10:02:37 +00:00
|
|
|
type->internalName());
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.body
|
2022-03-21 09:21:18 +00:00
|
|
|
<< u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
|
|
|
|
current.externalCtor.body << u"creator.set(0, this);"_s; // special case
|
2021-10-22 10:02:37 +00:00
|
|
|
// now call init
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.body << current.init.name
|
2021-10-22 10:02:37 +00:00
|
|
|
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
|
2022-03-03 13:03:58 +00:00
|
|
|
u"endInit */ true);";
|
2021-10-22 10:02:37 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
current.endInit.body << u"Q_UNUSED(canFinalize);"_s;
|
2021-10-22 10:02:37 +00:00
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
current.externalCtor.body << u"// not document root:"_s;
|
2021-10-22 10:02:37 +00:00
|
|
|
// just call init, we don't do any setup here otherwise
|
2022-03-03 13:03:58 +00:00
|
|
|
current.externalCtor.body << current.init.name
|
2021-10-22 10:02:37 +00:00
|
|
|
+ u"(creator, engine, QQmlData::get(parent)->outerContext);";
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
2021-11-12 08:28:33 +00:00
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
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 ¤t, const QQmlJSScope::ConstPtr &type)
|
|
|
|
{
|
|
|
|
const CodeGenerator::CodeGenObject &object = m_prototypeCodegen->objectFromType(type);
|
|
|
|
|
2021-10-29 10:43:44 +00:00
|
|
|
// compile components of a type:
|
|
|
|
// - enums
|
|
|
|
// - properties
|
|
|
|
// - methods
|
|
|
|
// - bindings
|
|
|
|
|
|
|
|
const auto enums = type->ownEnumerations();
|
|
|
|
current.enums.reserve(enums.size());
|
|
|
|
for (auto it = enums.begin(); it != enums.end(); ++it)
|
|
|
|
compileEnum(current, it.value());
|
2021-11-01 15:18:30 +00:00
|
|
|
|
2021-10-29 09:56:25 +00:00
|
|
|
auto properties = type->ownProperties().values();
|
2021-11-19 12:04:05 +00:00
|
|
|
current.properties.reserve(properties.size());
|
2021-10-29 09:56:25 +00:00
|
|
|
// 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(),
|
|
|
|
[](const QQmlJSMetaProperty &x, const QQmlJSMetaProperty &y) {
|
|
|
|
return x.index() < y.index();
|
|
|
|
});
|
|
|
|
for (const QQmlJSMetaProperty &p : qAsConst(properties)) {
|
|
|
|
if (p.index() == -1) {
|
|
|
|
recordError(type->sourceLocation(),
|
2022-03-21 09:21:18 +00:00
|
|
|
u"Internal error: property '%1' has incomplete information"_s.arg(
|
2021-10-29 09:56:25 +00:00
|
|
|
p.propertyName()));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (p.isAlias()) {
|
2022-04-22 10:51:41 +00:00
|
|
|
compileAlias(current, p, type);
|
2021-10-29 09:56:25 +00:00
|
|
|
} else {
|
|
|
|
compileProperty(current, p, type);
|
|
|
|
}
|
|
|
|
}
|
2021-11-15 16:10:31 +00:00
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
const auto methods = type->ownMethods();
|
2022-04-19 11:35:36 +00:00
|
|
|
for (const QQmlJSMetaMethod &m : methods)
|
|
|
|
compileMethod(current, m, type);
|
2021-11-19 12:04:05 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
const auto sortedBindings = CodeGenerator::toOrderedSequence(
|
|
|
|
object.irObject->bindingsBegin(), object.irObject->bindingsEnd(),
|
|
|
|
object.irObject->bindingCount());
|
|
|
|
|
|
|
|
auto scriptBindingsBegin =
|
|
|
|
std::find_if(sortedBindings.cbegin(), sortedBindings.cend(),
|
2022-05-04 13:26:30 +00:00
|
|
|
[](auto it) { return it->type() == QmlIR::Binding::Type_Script; });
|
2021-11-19 12:04:05 +00:00
|
|
|
auto it = sortedBindings.cbegin();
|
|
|
|
for (; it != scriptBindingsBegin; ++it) {
|
|
|
|
m_prototypeCodegen->compileBinding(current, **it, object,
|
2022-03-21 09:21:18 +00:00
|
|
|
{ object.type, u"this"_s, u""_s, false });
|
2021-11-19 12:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != sortedBindings.cend(); ++it) {
|
|
|
|
m_prototypeCodegen->compileBinding(current, **it, object,
|
2022-03-21 09:21:18 +00:00
|
|
|
{ type, u"this"_s, u""_s, false });
|
2021-11-19 12:04:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-29 10:43:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCompiler::compileEnum(QmltcType ¤t, 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),
|
2022-03-21 09:21:18 +00:00
|
|
|
u"Q_ENUM(%1)"_s.arg(e.name()));
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 15:18:30 +00:00
|
|
|
static QList<QmltcVariable>
|
|
|
|
compileMethodParameters(const QStringList &names,
|
|
|
|
const QList<QSharedPointer<const QQmlJSScope>> &types,
|
|
|
|
bool allowUnnamed = false)
|
|
|
|
{
|
|
|
|
QList<QmltcVariable> parameters;
|
|
|
|
const auto size = names.size();
|
|
|
|
parameters.reserve(size);
|
|
|
|
for (qsizetype i = 0; i < size; ++i) {
|
|
|
|
Q_ASSERT(types[i]); // assume verified
|
|
|
|
QString name = names[i];
|
|
|
|
Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified
|
|
|
|
if (name.isEmpty() && allowUnnamed)
|
|
|
|
name = u"unnamed_" + QString::number(i);
|
2021-12-17 14:52:17 +00:00
|
|
|
parameters.emplaceBack(types[i]->augmentedInternalName(), name, QString());
|
2021-11-01 15:18:30 +00:00
|
|
|
}
|
|
|
|
return parameters;
|
|
|
|
}
|
|
|
|
|
2022-04-19 11:35:36 +00:00
|
|
|
void QmltcCompiler::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m,
|
|
|
|
const QQmlJSScope::ConstPtr &owner)
|
2021-11-01 15:18:30 +00:00
|
|
|
{
|
|
|
|
const auto figureReturnType = [](const QQmlJSMetaMethod &m) {
|
|
|
|
const bool isVoidMethod =
|
|
|
|
m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal;
|
|
|
|
Q_ASSERT(isVoidMethod || m.returnType());
|
|
|
|
QString type;
|
|
|
|
if (isVoidMethod) {
|
2022-03-21 09:21:18 +00:00
|
|
|
type = u"void"_s;
|
2021-11-01 15:18:30 +00:00
|
|
|
} else {
|
2021-12-17 14:52:17 +00:00
|
|
|
type = m.returnType()->augmentedInternalName();
|
2021-11-01 15:18:30 +00:00
|
|
|
}
|
|
|
|
return type;
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto returnType = figureReturnType(m);
|
|
|
|
const auto paramNames = m.parameterNames();
|
|
|
|
const auto paramTypes = m.parameterTypes();
|
|
|
|
Q_ASSERT(paramNames.size() == paramTypes.size()); // assume verified
|
|
|
|
const QList<QmltcVariable> compiledParams = compileMethodParameters(paramNames, paramTypes);
|
|
|
|
const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
|
|
|
|
|
|
|
|
QStringList code;
|
|
|
|
if (methodType != QQmlJSMetaMethod::Signal) {
|
2022-04-19 11:35:36 +00:00
|
|
|
const qsizetype index =
|
|
|
|
static_cast<qsizetype>(owner->ownRuntimeFunctionIndex(m.jsFunctionIndex()));
|
|
|
|
Q_ASSERT(index >= 0);
|
|
|
|
QmltcCodeGenerator urlGenerator { m_url, m_visitor };
|
|
|
|
QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
|
2022-03-21 09:21:18 +00:00
|
|
|
&code, urlGenerator.urlMethodName() + u"()", index, u"this"_s, returnType,
|
2022-04-19 11:35:36 +00:00
|
|
|
compiledParams);
|
2021-11-01 15:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QmltcMethod compiled {};
|
|
|
|
compiled.returnType = returnType;
|
|
|
|
compiled.name = m.methodName();
|
|
|
|
compiled.parameterList = std::move(compiledParams);
|
|
|
|
compiled.body = std::move(code);
|
|
|
|
compiled.type = methodType;
|
|
|
|
compiled.access = m.access();
|
2022-04-19 11:35:36 +00:00
|
|
|
if (methodType != QQmlJSMetaMethod::Signal) {
|
2022-03-21 09:21:18 +00:00
|
|
|
compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
|
2022-04-19 11:35:36 +00:00
|
|
|
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
|
|
|
|
} else {
|
|
|
|
compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal();
|
|
|
|
}
|
2021-11-01 15:18:30 +00:00
|
|
|
current.functions.emplaceBack(compiled);
|
|
|
|
}
|
|
|
|
|
2021-10-29 09:56:25 +00:00
|
|
|
void QmltcCompiler::compileProperty(QmltcType ¤t, 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());
|
2022-03-03 13:03:58 +00:00
|
|
|
current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
|
|
|
|
+ u"(this, std::addressof(" + storageName
|
|
|
|
+ u")))");
|
2021-10-29 09:56:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
QmltcPropertyData compilationData(p);
|
|
|
|
|
2021-10-29 09:56:25 +00:00
|
|
|
// 1. add setter and getter
|
2021-11-19 12:04:05 +00:00
|
|
|
// 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()) {
|
2021-10-29 09:56:25 +00:00
|
|
|
QmltcMethod setter {};
|
2022-03-21 09:21:18 +00:00
|
|
|
setter.returnType = u"void"_s;
|
2021-11-19 12:04:05 +00:00
|
|
|
setter.name = compilationData.write;
|
|
|
|
// QmltcVariable
|
2021-12-17 14:52:17 +00:00
|
|
|
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
|
2022-03-21 09:21:18 +00:00
|
|
|
u""_s);
|
2021-10-29 09:56:25 +00:00
|
|
|
setter.body << variableName + u".setValue(" + name + u"_);";
|
2021-11-19 12:04:05 +00:00
|
|
|
setter.body << u"Q_EMIT " + compilationData.notify + u"();";
|
|
|
|
setter.userVisible = true;
|
2021-10-29 09:56:25 +00:00
|
|
|
current.functions.emplaceBack(setter);
|
2022-03-21 09:21:18 +00:00
|
|
|
mocPieces << u"WRITE"_s << setter.name;
|
2021-10-29 09:56:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QmltcMethod getter {};
|
|
|
|
getter.returnType = underlyingType;
|
2021-11-19 12:04:05 +00:00
|
|
|
getter.name = compilationData.read;
|
2021-10-29 09:56:25 +00:00
|
|
|
getter.body << u"return " + variableName + u".value();";
|
2021-11-19 12:04:05 +00:00
|
|
|
getter.userVisible = true;
|
2021-10-29 09:56:25 +00:00
|
|
|
current.functions.emplaceBack(getter);
|
2022-03-21 09:21:18 +00:00
|
|
|
mocPieces << u"READ"_s << getter.name;
|
2021-10-29 09:56:25 +00:00
|
|
|
|
|
|
|
// 2. add bindable
|
2021-11-19 12:04:05 +00:00
|
|
|
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);
|
2022-03-21 09:21:18 +00:00
|
|
|
mocPieces << u"BINDABLE"_s << bindable.name;
|
2021-11-19 12:04:05 +00:00
|
|
|
}
|
2021-10-29 09:56:25 +00:00
|
|
|
|
|
|
|
// 3. add/check notify (actually, this is already done inside QmltcVisitor)
|
|
|
|
|
|
|
|
if (owner->isPropertyRequired(name))
|
2022-03-21 09:21:18 +00:00
|
|
|
mocPieces << u"REQUIRED"_s;
|
2021-10-29 09:56:25 +00:00
|
|
|
|
|
|
|
// 4. add moc entry
|
|
|
|
// e.g. Q_PROPERTY(QString p READ getP WRITE setP BINDABLE bindableP)
|
2022-03-21 09:21:18 +00:00
|
|
|
current.mocCode << u"Q_PROPERTY(" + mocPieces.join(u" "_s) + u")";
|
2021-10-29 09:56:25 +00:00
|
|
|
|
|
|
|
// 5. add extra moc entry if this property is marked default
|
|
|
|
if (name == owner->defaultPropertyName())
|
2022-03-21 09:21:18 +00:00
|
|
|
current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(name);
|
2021-10-29 09:56:25 +00:00
|
|
|
|
|
|
|
// structure: (C++ type name, name, C++ class name, C++ signal name)
|
2021-11-19 12:04:05 +00:00
|
|
|
current.properties.emplaceBack(underlyingType, variableName, current.cppType,
|
|
|
|
compilationData.notify);
|
2021-10-29 09:56:25 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 10:51:41 +00:00
|
|
|
struct AliasResolutionFrame
|
|
|
|
{
|
|
|
|
static QString inVar;
|
|
|
|
QStringList prologue;
|
|
|
|
QStringList epilogueForWrite;
|
|
|
|
QString outVar;
|
|
|
|
};
|
|
|
|
// special string replaced by outVar of the previous frame
|
|
|
|
QString AliasResolutionFrame::inVar = QStringLiteral("__QMLTC_ALIAS_FRAME_INPUT_VAR__");
|
|
|
|
|
|
|
|
static void unpackFrames(QStack<AliasResolutionFrame> &frames)
|
|
|
|
{
|
|
|
|
if (frames.size() < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// assume first frame is fine
|
|
|
|
auto prev = frames.begin();
|
|
|
|
for (auto it = std::next(prev); it != frames.end(); ++it, ++prev) {
|
|
|
|
for (QString &line : it->prologue)
|
|
|
|
line.replace(AliasResolutionFrame::inVar, prev->outVar);
|
|
|
|
for (QString &line : it->epilogueForWrite)
|
|
|
|
line.replace(AliasResolutionFrame::inVar, prev->outVar);
|
|
|
|
it->outVar.replace(AliasResolutionFrame::inVar, prev->outVar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Projection>
|
|
|
|
static QStringList joinFrames(const QStack<AliasResolutionFrame> &frames, Projection project)
|
|
|
|
{
|
|
|
|
QStringList joined;
|
|
|
|
for (const AliasResolutionFrame &frame : frames)
|
|
|
|
joined += project(frame);
|
|
|
|
return joined;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias,
|
|
|
|
const QQmlJSScope::ConstPtr &owner)
|
|
|
|
{
|
|
|
|
const QString aliasName = alias.propertyName();
|
|
|
|
Q_ASSERT(!aliasName.isEmpty());
|
|
|
|
|
|
|
|
QStringList aliasExprBits = alias.aliasExpression().split(u'.');
|
|
|
|
Q_ASSERT(!aliasExprBits.isEmpty());
|
|
|
|
|
|
|
|
QStack<AliasResolutionFrame> frames;
|
|
|
|
|
|
|
|
QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
|
|
|
|
qsizetype i = 0;
|
|
|
|
aliasVisitor.reset = [&]() {
|
|
|
|
frames.clear();
|
|
|
|
i = 0; // we use it in property processing
|
|
|
|
|
|
|
|
// first frame is a dummy one:
|
|
|
|
frames.push(AliasResolutionFrame { QStringList(), QStringList(), u"this"_s });
|
|
|
|
};
|
|
|
|
aliasVisitor.processResolvedId = [&](const QQmlJSScope::ConstPtr &type) {
|
|
|
|
Q_ASSERT(type);
|
|
|
|
if (owner != type) { // cannot start at `this`, need to fetch object through context
|
|
|
|
const int id = m_visitor->runtimeId(type);
|
|
|
|
Q_ASSERT(id >= 0); // since the type is found by id, it must have an id
|
|
|
|
|
|
|
|
AliasResolutionFrame queryIdFrame {};
|
|
|
|
queryIdFrame.prologue << u"auto context = QQmlData::get(%1)->outerContext;"_s.arg(
|
|
|
|
AliasResolutionFrame::inVar);
|
|
|
|
// there's a special case: when `this` type has compiled QML type as
|
|
|
|
// a base type and it is not a root, it has a non-root first
|
|
|
|
// context, so we need to step one level up
|
|
|
|
if (QQmlJSUtils::hasCompositeBase(owner) && owner != m_visitor->result()) {
|
|
|
|
Q_ASSERT(!owner->baseTypeName().isEmpty());
|
|
|
|
queryIdFrame.prologue
|
|
|
|
<< u"// `this` is special: not a root and its base type is compiled"_s;
|
|
|
|
queryIdFrame.prologue << u"context = context->parent().data();"_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// doing the above allows us to lookup id object by index (fast)
|
|
|
|
queryIdFrame.outVar = u"alias_objectById_" + aliasExprBits.front(); // unique enough
|
|
|
|
queryIdFrame.prologue << u"auto " + queryIdFrame.outVar + u" = static_cast<"
|
|
|
|
+ type->internalName() + u"*>(context->idValue(" + QString::number(id)
|
|
|
|
+ u"));";
|
|
|
|
queryIdFrame.prologue << u"Q_ASSERT(" + queryIdFrame.outVar + u");";
|
|
|
|
|
|
|
|
frames.push(queryIdFrame);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
aliasVisitor.processResolvedProperty = [&](const QQmlJSMetaProperty &p,
|
|
|
|
const QQmlJSScope::ConstPtr &) {
|
|
|
|
AliasResolutionFrame queryPropertyFrame {};
|
|
|
|
|
|
|
|
QString inVar = QmltcCodeGenerator::wrap_privateClass(AliasResolutionFrame::inVar, p);
|
|
|
|
if (p.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Value) {
|
|
|
|
// we need to read the property to a local variable and then
|
|
|
|
// write the updated value once the actual operation is done
|
|
|
|
const QString aliasVar = u"alias_" + QString::number(i); // should be fairly unique
|
|
|
|
++i;
|
|
|
|
queryPropertyFrame.prologue
|
|
|
|
<< u"auto " + aliasVar + u" = " + inVar + u"->" + p.read() + u"();";
|
|
|
|
queryPropertyFrame.epilogueForWrite
|
|
|
|
<< inVar + u"->" + p.write() + u"(" + aliasVar + u");";
|
|
|
|
// NB: since accessor becomes a value type, wrap it into an
|
|
|
|
// addressof operator so that we could access it as a pointer
|
|
|
|
inVar = QmltcCodeGenerator::wrap_addressof(aliasVar); // reset
|
|
|
|
} else {
|
|
|
|
inVar += u"->" + p.read() + u"()"; // update
|
|
|
|
}
|
|
|
|
queryPropertyFrame.outVar = inVar;
|
|
|
|
|
|
|
|
frames.push(queryPropertyFrame);
|
|
|
|
};
|
|
|
|
|
|
|
|
QQmlJSUtils::ResolvedAlias result =
|
|
|
|
QQmlJSUtils::resolveAlias(m_typeResolver, alias, owner, aliasVisitor);
|
|
|
|
Q_ASSERT(result.kind != QQmlJSUtils::AliasTarget_Invalid);
|
|
|
|
|
|
|
|
unpackFrames(frames);
|
|
|
|
|
|
|
|
if (result.kind == QQmlJSUtils::AliasTarget_Property) {
|
|
|
|
// we don't need the last frame here
|
|
|
|
frames.pop();
|
|
|
|
|
|
|
|
// instead, add a custom frame
|
|
|
|
AliasResolutionFrame customFinalFrame {};
|
|
|
|
customFinalFrame.outVar =
|
|
|
|
QmltcCodeGenerator::wrap_privateClass(frames.top().outVar, result.property);
|
|
|
|
frames.push(customFinalFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString latestAccessor = frames.top().outVar;
|
|
|
|
const QStringList prologue =
|
|
|
|
joinFrames(frames, [](const AliasResolutionFrame &frame) { return frame.prologue; });
|
|
|
|
const QString underlyingType = (result.kind == QQmlJSUtils::AliasTarget_Property)
|
|
|
|
? getUnderlyingType(result.property)
|
|
|
|
: result.owner->internalName() + u" *";
|
|
|
|
|
|
|
|
QStringList mocLines;
|
|
|
|
mocLines.reserve(10);
|
|
|
|
mocLines << underlyingType << aliasName;
|
|
|
|
|
|
|
|
QmltcPropertyData compilationData(aliasName);
|
|
|
|
// 1. add setter and getter
|
|
|
|
QmltcMethod getter {};
|
|
|
|
getter.returnType = underlyingType;
|
|
|
|
getter.name = compilationData.read;
|
|
|
|
getter.body += prologue;
|
|
|
|
if (result.kind == QQmlJSUtils::AliasTarget_Property)
|
|
|
|
getter.body << u"return " + latestAccessor + u"->" + result.property.read() + u"();";
|
|
|
|
else // AliasTarget_Object
|
|
|
|
getter.body << u"return " + latestAccessor + u";";
|
|
|
|
getter.userVisible = true;
|
|
|
|
current.functions.emplaceBack(getter);
|
|
|
|
mocLines << u"READ"_s << getter.name;
|
|
|
|
|
|
|
|
if (QString setName = result.property.write(); !setName.isEmpty()) {
|
|
|
|
Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
|
|
|
|
QmltcMethod setter {};
|
|
|
|
setter.returnType = u"void"_s;
|
|
|
|
setter.name = compilationData.write;
|
|
|
|
|
|
|
|
QList<QQmlJSMetaMethod> methods = result.owner->methods(setName);
|
|
|
|
if (methods.isEmpty()) { // when we are compiling the property as well
|
|
|
|
// QmltcVariable
|
|
|
|
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType),
|
|
|
|
aliasName + u"_", u""_s);
|
|
|
|
} else {
|
|
|
|
setter.parameterList = compileMethodParameters(methods.at(0).parameterNames(),
|
|
|
|
methods.at(0).parameterTypes(),
|
|
|
|
/* allow unnamed = */ true);
|
|
|
|
}
|
|
|
|
|
|
|
|
setter.body += prologue;
|
|
|
|
QStringList parameterNames;
|
|
|
|
parameterNames.reserve(setter.parameterList.size());
|
|
|
|
std::transform(setter.parameterList.cbegin(), setter.parameterList.cend(),
|
|
|
|
std::back_inserter(parameterNames),
|
|
|
|
[](const QmltcVariable &x) { return x.name; });
|
|
|
|
QString commaSeparatedParameterNames = parameterNames.join(u", "_s);
|
|
|
|
setter.body << latestAccessor + u"->" + setName
|
|
|
|
+ u"(%1)"_s.arg(commaSeparatedParameterNames) + u";";
|
|
|
|
setter.body += joinFrames(
|
|
|
|
frames, [](const AliasResolutionFrame &frame) { return frame.epilogueForWrite; });
|
|
|
|
setter.userVisible = true;
|
|
|
|
current.functions.emplaceBack(setter);
|
|
|
|
mocLines << u"WRITE"_s << setter.name;
|
|
|
|
}
|
|
|
|
// 2. add bindable
|
|
|
|
if (QString bindableName = result.property.bindable(); !bindableName.isEmpty()) {
|
|
|
|
Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
|
|
|
|
QmltcMethod bindable {};
|
|
|
|
bindable.returnType = u"QBindable<" + underlyingType + u">";
|
|
|
|
bindable.name = compilationData.bindable;
|
|
|
|
bindable.body += prologue;
|
|
|
|
bindable.body << u"return " + latestAccessor + u"->" + bindableName + u"()" + u";";
|
|
|
|
bindable.userVisible = true;
|
|
|
|
current.functions.emplaceBack(bindable);
|
|
|
|
mocLines << u"BINDABLE"_s << bindable.name;
|
|
|
|
}
|
|
|
|
// 3. add notify - which is pretty special
|
|
|
|
if (QString notifyName = result.property.notify(); !notifyName.isEmpty()) {
|
|
|
|
Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
|
|
|
|
|
|
|
|
// notify is very special
|
|
|
|
current.endInit.body << u"{ // alias notify connection:"_s;
|
|
|
|
current.endInit.body += prologue;
|
|
|
|
// TODO: use non-private accessor since signals must exist on the public
|
|
|
|
// type, not on the private one -- otherwise, you can't connect to a
|
|
|
|
// private property signal in C++ and so it is useless (hence, use
|
|
|
|
// public type)
|
|
|
|
const QString latestAccessorNonPrivate = frames[frames.size() - 2].outVar;
|
|
|
|
current.endInit.body << u"QObject::connect(" + latestAccessorNonPrivate + u", &"
|
|
|
|
+ result.owner->internalName() + u"::" + notifyName + u", this, &"
|
|
|
|
+ current.cppType + u"::" + compilationData.notify + u");";
|
|
|
|
current.endInit.body << u"}"_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4. add moc entry
|
|
|
|
// Q_PROPERTY(QString text READ text WRITE setText BINDABLE bindableText NOTIFY textChanged)
|
|
|
|
current.mocCode << u"Q_PROPERTY(" + mocLines.join(u" "_s) + u")";
|
|
|
|
|
|
|
|
// 5. add extra moc entry if this alias is default one
|
|
|
|
if (aliasName == owner->defaultPropertyName()) {
|
|
|
|
// Q_CLASSINFO("DefaultProperty", propertyName)
|
|
|
|
current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(aliasName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 16:10:31 +00:00
|
|
|
void QmltcCompiler::compileBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding,
|
|
|
|
const QQmlJSScope::ConstPtr &type,
|
|
|
|
const BindingAccessorData &accessor)
|
|
|
|
{
|
|
|
|
Q_UNUSED(current);
|
|
|
|
Q_UNUSED(accessor);
|
|
|
|
QString propertyName = binding.propertyName();
|
|
|
|
if (propertyName.isEmpty()) {
|
|
|
|
// if empty, try default property
|
|
|
|
for (QQmlJSScope::ConstPtr t = type->baseType(); t && propertyName.isEmpty();
|
|
|
|
t = t->baseType()) {
|
|
|
|
propertyName = t->defaultPropertyName();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q_ASSERT(!propertyName.isEmpty());
|
|
|
|
QQmlJSMetaProperty p = type->property(propertyName);
|
|
|
|
Q_ASSERT(p.isValid());
|
|
|
|
QQmlJSScope::ConstPtr propertyType = p.type();
|
|
|
|
Q_ASSERT(propertyType);
|
|
|
|
|
|
|
|
// NB: we assume here that QmltcVisitor took care of type mismatches and
|
|
|
|
// other errors, so the compiler just needs to add correct instructions,
|
|
|
|
// without if-checking every type
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
QmltcCodeGenerator generator {};
|
2021-11-15 16:10:31 +00:00
|
|
|
|
|
|
|
switch (binding.bindingType()) {
|
|
|
|
case QQmlJSMetaPropertyBinding::BoolLiteral: {
|
Refactor QQmlJSMetaPropertyBinding
- Store the "binding content", i.e. the literal, object, interceptor or
value source in a std::variant. This saves space compared to the
previous approach (where we had individual fields), and also helps
with type-safety. We also get rid of m_bindingType, which can now be
obtained via BindingType(m_bindingContent.index()), as long as we keep
the types in the variant in the correct order.
- Remove a few methods that were not used anywhere. Those can be brought
back once we actually need them.
- Removed the setLiteral method in lieu of type-safe setX methods where
X is one of the literal types. QQmlJSImportVisitor has been refactored
to make use of this, and its parseLiteralBinding method has been
changed into a parseLiteralOrScriptBinding method. This simplifies the
control flow, and ensures that we always add the parsed binding.
- Literals no longer store the literal type (as in, the actual
QQmlJSScope pointer) themselves. Instead, literalType takes a pointer
to a QQmlJSTypeResolver, and we use that one to resolve the type on
demand.
Change-Id: I0612d49f2f46fec0fa90f0f5047d8c9f831214ef
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
2022-02-24 10:58:38 +00:00
|
|
|
const bool value = binding.boolValue();
|
2022-03-09 10:56:09 +00:00
|
|
|
generator.generate_assignToProperty(¤t.init.body, type, p,
|
2022-03-21 09:21:18 +00:00
|
|
|
value ? u"true"_s : u"false"_s, accessor.name);
|
2021-11-15 16:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSMetaPropertyBinding::NumberLiteral: {
|
Refactor QQmlJSMetaPropertyBinding
- Store the "binding content", i.e. the literal, object, interceptor or
value source in a std::variant. This saves space compared to the
previous approach (where we had individual fields), and also helps
with type-safety. We also get rid of m_bindingType, which can now be
obtained via BindingType(m_bindingContent.index()), as long as we keep
the types in the variant in the correct order.
- Remove a few methods that were not used anywhere. Those can be brought
back once we actually need them.
- Removed the setLiteral method in lieu of type-safe setX methods where
X is one of the literal types. QQmlJSImportVisitor has been refactored
to make use of this, and its parseLiteralBinding method has been
changed into a parseLiteralOrScriptBinding method. This simplifies the
control flow, and ensures that we always add the parsed binding.
- Literals no longer store the literal type (as in, the actual
QQmlJSScope pointer) themselves. Instead, literalType takes a pointer
to a QQmlJSTypeResolver, and we use that one to resolve the type on
demand.
Change-Id: I0612d49f2f46fec0fa90f0f5047d8c9f831214ef
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
2022-02-24 10:58:38 +00:00
|
|
|
const QString value = QString::number(binding.numberValue());
|
2022-03-09 10:56:09 +00:00
|
|
|
generator.generate_assignToProperty(¤t.init.body, type, p, value, accessor.name);
|
2021-11-15 16:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSMetaPropertyBinding::StringLiteral: {
|
Refactor QQmlJSMetaPropertyBinding
- Store the "binding content", i.e. the literal, object, interceptor or
value source in a std::variant. This saves space compared to the
previous approach (where we had individual fields), and also helps
with type-safety. We also get rid of m_bindingType, which can now be
obtained via BindingType(m_bindingContent.index()), as long as we keep
the types in the variant in the correct order.
- Remove a few methods that were not used anywhere. Those can be brought
back once we actually need them.
- Removed the setLiteral method in lieu of type-safe setX methods where
X is one of the literal types. QQmlJSImportVisitor has been refactored
to make use of this, and its parseLiteralBinding method has been
changed into a parseLiteralOrScriptBinding method. This simplifies the
control flow, and ensures that we always add the parsed binding.
- Literals no longer store the literal type (as in, the actual
QQmlJSScope pointer) themselves. Instead, literalType takes a pointer
to a QQmlJSTypeResolver, and we use that one to resolve the type on
demand.
Change-Id: I0612d49f2f46fec0fa90f0f5047d8c9f831214ef
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
2022-02-24 10:58:38 +00:00
|
|
|
const QString value = binding.stringValue();
|
2022-03-09 10:56:09 +00:00
|
|
|
generator.generate_assignToProperty(¤t.init.body, type, p,
|
|
|
|
QQmlJSUtils::toLiteral(value), accessor.name);
|
2021-11-15 16:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSMetaPropertyBinding::Null: {
|
|
|
|
// poor check: null bindings are only supported for var and objects
|
|
|
|
if (propertyType != m_typeResolver->varType()
|
|
|
|
&& propertyType->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
|
|
|
|
// TODO: this should really be done before the compiler here
|
|
|
|
recordError(binding.sourceLocation(),
|
2022-03-21 09:21:18 +00:00
|
|
|
u"Cannot assign null to incompatible property"_s);
|
2021-11-15 16:10:31 +00:00
|
|
|
} else if (propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
|
2022-03-21 09:21:18 +00:00
|
|
|
generator.generate_assignToProperty(¤t.init.body, type, p, u"nullptr"_s,
|
2022-03-09 10:56:09 +00:00
|
|
|
accessor.name);
|
2021-11-15 16:10:31 +00:00
|
|
|
} else {
|
2022-03-09 10:56:09 +00:00
|
|
|
generator.generate_assignToProperty(¤t.init.body, type, p,
|
2022-03-21 09:21:18 +00:00
|
|
|
u"QVariant::fromValue(nullptr)"_s, accessor.name);
|
2021-11-15 16:10:31 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// case QQmlJSMetaPropertyBinding::RegExpLiteral:
|
|
|
|
// case QQmlJSMetaPropertyBinding::Translation:
|
|
|
|
// case QQmlJSMetaPropertyBinding::TranslationById:
|
|
|
|
// case QQmlJSMetaPropertyBinding::Script:
|
|
|
|
// case QQmlJSMetaPropertyBinding::Object:
|
|
|
|
// case QQmlJSMetaPropertyBinding::Interceptor:
|
|
|
|
// case QQmlJSMetaPropertyBinding::ValueSource:
|
|
|
|
// case QQmlJSMetaPropertyBinding::AttachedProperty:
|
|
|
|
// case QQmlJSMetaPropertyBinding::GroupProperty:
|
|
|
|
case QQmlJSMetaPropertyBinding::Invalid: {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_logger->log(u"This binding is invalid"_s, Log_Compiler, binding.sourceLocation());
|
2021-11-15 16:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_logger->log(u"Binding type is not supported (yet)"_s, Log_Compiler,
|
Redesign QQmlJSLogger internals
High-level goal: be able to reuse existing infrastructure
"as is" to configure semantic analysis categories in tools
(qmllint, qmltc, qmlsc, etc.)
To achieve that, simplify the logging to always "log"
something, without explicitly specifying the severity. The
severity is now baked into the category (and we can extend
those to cover different cases)
One slight deviation is the cache generation which likes
to do its own thing at present. Provide a "forced logging"
option where we can specify which severify we want. The
hope is that this gets removed at some point
Particular list of (noteworthy) changes:
* No more "thresholding" by the level (this is rarely needed
and is actually questionable). Instead, we can ignore a
particular category explicitly
* Category levels are repurposed as category severities
(at least from the high-level picture that always should've
been this way)
* log{Warning,Info,Critical} removed. We use category severity
instead
* "category error" makes zero sense so removed: if our severity
is:
- QtWarningMsg (qmllint), it is already an "error"
- QtCriticalMsg (compilers), it is already an "error"
* Align m_output and m_{infos,warnings,errors} stored information
* Accept the fact that we don't support QtDebugMsg and QtFatalMsg
* Additional categories added to cover for places where the same
category would be both an error and not an error
Task-number: QTBUG-100052
Change-Id: I3cd5d17d58be204f48428877bed053f756ac40a8
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-02-03 12:45:18 +00:00
|
|
|
binding.sourceLocation());
|
2021-11-15 16:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 07:35:33 +00:00
|
|
|
QT_END_NAMESPACE
|