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 | |
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 ¤t, const QQmlJSScope::ConstPtr &type, |
72 | std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements); |
73 | void compileTypeElements(QmltcType ¤t, const QQmlJSScope::ConstPtr &type); |
74 | void compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e); |
75 | void compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, |
76 | const QQmlJSScope::ConstPtr &owner); |
77 | void compileProperty(QmltcType ¤t, const QQmlJSMetaProperty &p, |
78 | const QQmlJSScope::ConstPtr &owner); |
79 | void compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias, |
80 | const QQmlJSScope::ConstPtr &owner); |
81 | void (QmltcType ¤t, 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 ¤t, const QQmlJSScope::ConstPtr &type, |
106 | const BindingAccessorData &accessor); |
107 | |
108 | void compileBinding(QmltcType ¤t, QList<QQmlJSMetaPropertyBinding>::iterator bindingStart, |
109 | QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd, |
110 | const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor); |
111 | |
112 | void compileBindingByType(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
113 | const QQmlJSScope::ConstPtr &type, |
114 | const BindingAccessorData &accessor); |
115 | |
116 | void compileObjectBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
117 | const QQmlJSScope::ConstPtr &type, |
118 | const BindingAccessorData &accessor); |
119 | |
120 | void compileValueSourceOrInterceptorBinding(QmltcType ¤t, |
121 | const QQmlJSMetaPropertyBinding &binding, |
122 | const QQmlJSScope::ConstPtr &type, |
123 | const BindingAccessorData &accessor); |
124 | |
125 | void compileAttachedPropertyBinding(QmltcType ¤t, |
126 | const QQmlJSMetaPropertyBinding &binding, |
127 | const QQmlJSScope::ConstPtr &type, |
128 | const BindingAccessorData &accessor); |
129 | |
130 | void compileGroupPropertyBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
131 | const QQmlJSScope::ConstPtr &type, |
132 | const BindingAccessorData &accessor); |
133 | |
134 | void compileTranslationBinding(QmltcType ¤t, const QQmlJSMetaPropertyBinding &binding, |
135 | const QQmlJSScope::ConstPtr &type, |
136 | const BindingAccessorData &accessor); |
137 | |
138 | // special case (for simplicity) |
139 | void compileScriptBinding(QmltcType ¤t, 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 | |
204 | QT_END_NAMESPACE |
205 | |
206 | #endif // QMLTCCOMPILER_H |
207 | |