1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef QMLTCCOMPILER_H
5#define QMLTCCOMPILER_H
6
7#include "qmltctyperesolver.h"
8#include "qmltcvisitor.h"
9#include "qmltcoutputir.h"
10
11#include <QtCore/qcommandlineparser.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qstring.h>
14#include <QtCore/qhash.h>
15
16#include <private/qqmljslogger_p.h>
17
18#include <memory>
19
20QT_BEGIN_NAMESPACE
21
22struct QmltcCompilerInfo
23{
24 QString outputCppFile;
25 QString outputHFile;
26 QString outputNamespace;
27 QString resourcePath;
28 QString exportMacro;
29 QString exportInclude;
30};
31
32class QmltcCompiler
33{
34 using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
35 using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
36 using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
37
38public:
39 QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
40 QQmlJSLogger *logger);
41 void compile(const QmltcCompilerInfo &info);
42
43 ~QmltcCompiler();
44
45 /*! \internal
46
47 Returns \c true if \a binding is considered complex by the compiler
48 (requires special code generation)
49 */
50 static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding)
51 {
52 // TODO: translation bindings (once supported) are also complex?
53 return binding.bindingType() == QQmlSA::BindingType::Script;
54 }
55
56private:
57 QString m_url; // QML input file url
58 QmltcTypeResolver *m_typeResolver = nullptr;
59 QmltcVisitor *m_visitor = nullptr;
60 QQmlJSLogger *m_logger = nullptr;
61 QmltcCompilerInfo m_info {}; // miscellaneous input/output information
62 QString m_urlMethodName;
63
64 struct UniqueStringId;
65 struct QmltcTypeLocalData;
66 // per-type, per-property code generation cache of created symbols
67 QHash<UniqueStringId, QmltcTypeLocalData> m_uniques;
68
69 void compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName);
70 void
71 compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type,
72 std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements);
73 void compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type);
74 void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
75 void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
76 const QQmlJSScope::ConstPtr &owner);
77 void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
78 const QQmlJSScope::ConstPtr &owner);
79 void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
80 const QQmlJSScope::ConstPtr &owner);
81 void compileExtraListMethods(QmltcType &current, const QQmlJSMetaProperty &p);
82
83 /*!
84 \internal
85
86 Helper structure that holds the information necessary for most bindings,
87 such as accessor name, which is used to reference the properties. For
88 example:
89 > (accessor.name)->(propertyName) results in "this->myProperty"
90
91 This data is also used in more advanced scenarios by attached and
92 grouped properties
93 */
94 struct BindingAccessorData
95 {
96 QQmlJSScope::ConstPtr scope; // usually the current type
97 QString name = QStringLiteral("this");
98 QString propertyName = QString();
99 bool isValueType = false;
100 };
101
102 QStringList unprocessedListBindings;
103 QQmlJSMetaProperty unprocessedListProperty;
104
105 void processLastListBindings(QmltcType &current, const QQmlJSScope::ConstPtr &type,
106 const BindingAccessorData &accessor);
107
108 void compileBinding(QmltcType &current, QList<QQmlJSMetaPropertyBinding>::iterator bindingStart,
109 QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd,
110 const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor);
111
112 void compileBindingByType(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
113 const QQmlJSScope::ConstPtr &type,
114 const BindingAccessorData &accessor);
115
116 void compileObjectBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
117 const QQmlJSScope::ConstPtr &type,
118 const BindingAccessorData &accessor);
119
120 void compileValueSourceOrInterceptorBinding(QmltcType &current,
121 const QQmlJSMetaPropertyBinding &binding,
122 const QQmlJSScope::ConstPtr &type,
123 const BindingAccessorData &accessor);
124
125 void compileAttachedPropertyBinding(QmltcType &current,
126 const QQmlJSMetaPropertyBinding &binding,
127 const QQmlJSScope::ConstPtr &type,
128 const BindingAccessorData &accessor);
129
130 void compileGroupPropertyBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
131 const QQmlJSScope::ConstPtr &type,
132 const BindingAccessorData &accessor);
133
134 void compileTranslationBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
135 const QQmlJSScope::ConstPtr &type,
136 const BindingAccessorData &accessor);
137
138 // special case (for simplicity)
139 void compileScriptBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
140 const QString &bindingSymbolName, const QQmlJSScope::ConstPtr &type,
141 const QString &propertyName,
142 const QQmlJSScope::ConstPtr &propertyType,
143 const BindingAccessorData &accessor);
144
145 /*!
146 \internal
147 Helper structure that acts as a key in a hash-table of
148 QmltcType-specific data (such as local variable names). Using a
149 hash-table allows to avoid creating the same variables multiple times
150 during binding compilation, which leads to better code generation and
151 faster object creation. This is really something that the QML optimizer
152 should do, but we have only this home-grown alternative at the moment
153 */
154 struct UniqueStringId
155 {
156 QString unique;
157 UniqueStringId(const QmltcType &context, const QString &property)
158 : unique(context.cppType + u"_" + property) // this is unique enough
159 {
160 Q_ASSERT(!context.cppType.isEmpty());
161 Q_ASSERT(!property.isEmpty());
162 }
163 friend bool operator==(const UniqueStringId &x, const UniqueStringId &y)
164 {
165 return x.unique == y.unique;
166 }
167 friend bool operator!=(const UniqueStringId &x, const UniqueStringId &y)
168 {
169 return !(x == y);
170 }
171 friend size_t qHash(const UniqueStringId &x, size_t seed = 0)
172 {
173 return qHash(key: x.unique, seed);
174 }
175 };
176
177 struct QmltcTypeLocalData
178 {
179 // empty QString() means that the local data is not present (yet)
180 QString qmlListVariableName;
181 QString onAssignmentObjectName;
182 QString attachedVariableName;
183 };
184
185 QHash<QString, qsizetype> m_symbols;
186 QString newSymbol(const QString &base);
187
188 bool hasErrors() const { return m_logger->hasErrors(); }
189 void recordError(const QQmlJS::SourceLocation &location, const QString &message,
190 QQmlJS::LoggerWarningId id = qmlCompiler)
191 {
192 // pretty much any compiler error is a critical error (we cannot
193 // generate code - compilation fails)
194 m_logger->log(message, id, srcLocation: location);
195 }
196 void recordError(const QV4::CompiledData::Location &location, const QString &message,
197 QQmlJS::LoggerWarningId id = qmlCompiler)
198 {
199 recordError(location: QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message,
200 id);
201 }
202};
203
204QT_END_NAMESPACE
205
206#endif // QMLTCCOMPILER_H
207

source code of qtdeclarative/tools/qmltc/qmltccompiler.h