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 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | struct QmltcCompilerInfo |
23 | { |
24 | QString outputCppFile; |
25 | QString outputHFile; |
26 | QString outputNamespace; |
27 | QString resourcePath; |
28 | QString exportMacro; |
29 | QString exportInclude; |
30 | }; |
31 | |
32 | class QmltcCompiler |
33 | { |
34 | using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName; |
35 | using InlineComponentNameType = QQmlJSScope::InlineComponentNameType; |
36 | using RootDocumentNameType = QQmlJSScope::RootDocumentNameType; |
37 | |
38 | public: |
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 | |
56 | private: |
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 ¤t, const QQmlJSScope::ConstPtr &type, |
73 | std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements); |
74 | void compileTypeElements(QmltcType ¤t, const QQmlJSScope::ConstPtr &type); |
75 | void compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e); |
76 | void compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, |
77 | const QQmlJSScope::ConstPtr &owner); |
78 | void compileProperty(QmltcType ¤t, const QQmlJSMetaProperty &p, |
79 | const QQmlJSScope::ConstPtr &owner); |
80 | void compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias, |
81 | const QQmlJSScope::ConstPtr &owner); |
82 | void (QmltcType ¤t, 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 ¤t, const QQmlJSScope::ConstPtr &type, |
114 | const BindingAccessorData &accessor); |
115 | |
116 | void compileBinding(QmltcType ¤t, QList<QQmlJSMetaPropertyBinding>::iterator bindingStart, |
117 | QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd, |
118 | const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor); |
119 | |
120 | void compileBindingByType(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
121 | const QQmlJSScope::ConstPtr &type, |
122 | const BindingAccessorData &accessor); |
123 | |
124 | void compileObjectBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
125 | const QQmlJSScope::ConstPtr &type, |
126 | const BindingAccessorData &accessor); |
127 | |
128 | void compileValueSourceOrInterceptorBinding(QmltcType ¤t, |
129 | const QQmlJSMetaPropertyBinding &binding, |
130 | const QQmlJSScope::ConstPtr &type, |
131 | const BindingAccessorData &accessor); |
132 | |
133 | void compileAttachedPropertyBinding(QmltcType ¤t, |
134 | const QQmlJSMetaPropertyBinding &binding, |
135 | const QQmlJSScope::ConstPtr &type, |
136 | const BindingAccessorData &accessor); |
137 | |
138 | void compileGroupPropertyBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
139 | const QQmlJSScope::ConstPtr &type, |
140 | const BindingAccessorData &accessor); |
141 | |
142 | void compileTranslationBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
143 | const QQmlJSScope::ConstPtr &type, |
144 | const BindingAccessorData &accessor); |
145 | |
146 | // special case (for simplicity) |
147 | void compileScriptBinding(QmltcType ¤t, 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 | /*! |
154 | \internal |
155 | Helper structure that acts as a key in a hash-table of |
156 | QmltcType-specific data (such as local variable names). Using a |
157 | hash-table allows to avoid creating the same variables multiple times |
158 | during binding compilation, which leads to better code generation and |
159 | faster object creation. This is really something that the QML optimizer |
160 | should do, but we have only this home-grown alternative at the moment |
161 | */ |
162 | struct UniqueStringId |
163 | { |
164 | QString unique; |
165 | UniqueStringId(const QmltcType &context, const QString &property) |
166 | : unique(context.cppType + u"_" + property) // this is unique enough |
167 | { |
168 | Q_ASSERT(!context.cppType.isEmpty()); |
169 | Q_ASSERT(!property.isEmpty()); |
170 | } |
171 | friend bool operator==(const UniqueStringId &x, const UniqueStringId &y) |
172 | { |
173 | return x.unique == y.unique; |
174 | } |
175 | friend bool operator!=(const UniqueStringId &x, const UniqueStringId &y) |
176 | { |
177 | return !(x == y); |
178 | } |
179 | friend size_t qHash(const UniqueStringId &x, size_t seed = 0) |
180 | { |
181 | return qHash(key: x.unique, seed); |
182 | } |
183 | }; |
184 | |
185 | struct QmltcTypeLocalData |
186 | { |
187 | // empty QString() means that the local data is not present (yet) |
188 | QString qmlListVariableName; |
189 | QString onAssignmentObjectName; |
190 | QString attachedVariableName; |
191 | }; |
192 | |
193 | QHash<QString, qsizetype> m_symbols; |
194 | QString newSymbol(const QString &base); |
195 | |
196 | bool hasErrors() const { return m_logger->hasErrors(); } |
197 | void recordError(const QQmlJS::SourceLocation &location, const QString &message, |
198 | QQmlJS::LoggerWarningId id = qmlCompiler) |
199 | { |
200 | // pretty much any compiler error is a critical error (we cannot |
201 | // generate code - compilation fails) |
202 | m_logger->log(message, id, srcLocation: location); |
203 | } |
204 | void recordError(const QV4::CompiledData::Location &location, const QString &message, |
205 | QQmlJS::LoggerWarningId id = qmlCompiler) |
206 | { |
207 | recordError(location: QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message, |
208 | id); |
209 | } |
210 | }; |
211 | |
212 | QT_END_NAMESPACE |
213 | |
214 | #endif // QMLTCCOMPILER_H |
215 | |