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 uint m_currentVariableNumber = 0;
64
65 struct UniqueStringId;
66 struct QmltcTypeLocalData;
67 // per-type, per-property code generation cache of created symbols
68 QHash<UniqueStringId, QmltcTypeLocalData> m_uniques;
69
70 void compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName);
71 void
72 compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type,
73 std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements);
74 void compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type);
75 void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
76 void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
77 const QQmlJSScope::ConstPtr &owner);
78 void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
79 const QQmlJSScope::ConstPtr &owner);
80 void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
81 const QQmlJSScope::ConstPtr &owner);
82 void compileExtraListMethods(QmltcType &current, const QQmlJSMetaProperty &p);
83
84 QString uniqueVariableName(const QString &qmlName)
85 {
86 QString result = u"m_"_s + QString::number(++m_currentVariableNumber) + qmlName;
87 result.replace(before: u'.', after: u'_');
88 return result;
89 }
90
91 /*!
92 \internal
93
94 Helper structure that holds the information necessary for most bindings,
95 such as accessor name, which is used to reference the properties. For
96 example:
97 > (accessor.name)->(propertyName) results in "this->myProperty"
98
99 This data is also used in more advanced scenarios by attached and
100 grouped properties
101 */
102 struct BindingAccessorData
103 {
104 QQmlJSScope::ConstPtr scope; // usually the current type
105 QString name = QStringLiteral("this");
106 QString propertyName = QString();
107 bool isValueType = false;
108 };
109
110 QStringList unprocessedListBindings;
111 QQmlJSMetaProperty unprocessedListProperty;
112
113 void processLastListBindings(QmltcType &current, const QQmlJSScope::ConstPtr &type,
114 const BindingAccessorData &accessor);
115
116 void compileBinding(QmltcType &current, QList<QQmlJSMetaPropertyBinding>::iterator bindingStart,
117 QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd,
118 const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor);
119
120 void compileBindingByType(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
121 const QQmlJSScope::ConstPtr &type,
122 const BindingAccessorData &accessor);
123
124 void compileObjectBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
125 const QQmlJSScope::ConstPtr &type,
126 const BindingAccessorData &accessor);
127
128 void compileValueSourceOrInterceptorBinding(QmltcType &current,
129 const QQmlJSMetaPropertyBinding &binding,
130 const QQmlJSScope::ConstPtr &type,
131 const BindingAccessorData &accessor);
132
133 void compileAttachedPropertyBinding(QmltcType &current,
134 const QQmlJSMetaPropertyBinding &binding,
135 const QQmlJSScope::ConstPtr &type,
136 const BindingAccessorData &accessor);
137
138 void compileGroupPropertyBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
139 const QQmlJSScope::ConstPtr &type,
140 const BindingAccessorData &accessor);
141
142 void compileTranslationBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
143 const QQmlJSScope::ConstPtr &type,
144 const BindingAccessorData &accessor);
145
146 // special case (for simplicity)
147 void compileScriptBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
148 const QString &bindingSymbolName, const QQmlJSScope::ConstPtr &type,
149 const QString &propertyName,
150 const QQmlJSScope::ConstPtr &propertyType,
151 const BindingAccessorData &accessor);
152
153 void compilePropertyInitializer(QmltcType &current, const QQmlJSScope::ConstPtr &type);
154
155 /*!
156 \internal
157 Helper structure that acts as a key in a hash-table of
158 QmltcType-specific data (such as local variable names). Using a
159 hash-table allows to avoid creating the same variables multiple times
160 during binding compilation, which leads to better code generation and
161 faster object creation. This is really something that the QML optimizer
162 should do, but we have only this home-grown alternative at the moment
163 */
164 struct UniqueStringId
165 {
166 QString unique;
167 UniqueStringId(const QmltcType &context, const QString &property)
168 : unique(context.cppType + u"_" + property) // this is unique enough
169 {
170 Q_ASSERT(!context.cppType.isEmpty());
171 Q_ASSERT(!property.isEmpty());
172 }
173 friend bool operator==(const UniqueStringId &x, const UniqueStringId &y)
174 {
175 return x.unique == y.unique;
176 }
177 friend bool operator!=(const UniqueStringId &x, const UniqueStringId &y)
178 {
179 return !(x == y);
180 }
181 friend size_t qHash(const UniqueStringId &x, size_t seed = 0)
182 {
183 return qHash(key: x.unique, seed);
184 }
185 };
186
187 struct QmltcTypeLocalData
188 {
189 // empty QString() means that the local data is not present (yet)
190 QString qmlListVariableName;
191 QString onAssignmentObjectName;
192 QString attachedVariableName;
193 };
194
195 QHash<QString, qsizetype> m_symbols;
196 QString newSymbol(const QString &base);
197
198 bool hasErrors() const { return m_logger->hasErrors(); }
199 void recordError(const QQmlJS::SourceLocation &location, const QString &message,
200 QQmlJS::LoggerWarningId id = qmlCompiler)
201 {
202 // pretty much any compiler error is a critical error (we cannot
203 // generate code - compilation fails)
204 m_logger->log(message, id, srcLocation: location);
205 }
206 void recordError(const QV4::CompiledData::Location &location, const QString &message,
207 QQmlJS::LoggerWarningId id = qmlCompiler)
208 {
209 recordError(location: QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message,
210 id);
211 }
212};
213
214QT_END_NAMESPACE
215
216#endif // QMLTCCOMPILER_H
217

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