| 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 | void compilePropertyInitializer(QmltcType ¤t, 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 | |
| 214 | QT_END_NAMESPACE |
| 215 | |
| 216 | #endif // QMLTCCOMPILER_H |
| 217 | |