1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmldomtypesreader_p.h"
5#include "qqmldomelements_p.h"
6#include "qqmldomcompare_p.h"
7#include "qqmldomfieldfilter_p.h"
8
9#include <QtQml/private/qqmljsparser_p.h>
10#include <QtQml/private/qqmljslexer_p.h>
11#include <QtQml/private/qqmljsengine_p.h>
12#include <private/qqmljstypedescriptionreader_p.h>
13
14#include <QtCore/qdir.h>
15
16QT_BEGIN_NAMESPACE
17
18namespace QQmlJS {
19namespace Dom {
20
21using namespace QQmlJS::AST;
22
23static ErrorGroups readerParseErrors()
24{
25 static ErrorGroups errs = { .groups: { NewErrorGroup("Dom"), NewErrorGroup("QmltypesFile"),
26 NewErrorGroup("Parsing") } };
27 return errs;
28}
29
30void QmltypesReader::insertProperty(QQmlJSScope::Ptr jsScope, const QQmlJSMetaProperty &property,
31 QMap<int, QmlObject> &objs)
32{
33 PropertyDefinition prop;
34 prop.name = property.propertyName();
35 prop.typeName = property.typeName();
36 prop.isPointer = property.isPointer();
37 prop.isReadonly = !property.isWritable();
38 prop.isRequired = jsScope->isPropertyLocallyRequired(name: prop.name);
39 prop.isList = property.isList();
40 int revision = property.revision();
41 prop.isFinal = property.isFinal();
42 prop.bindable = property.bindable();
43 prop.read = property.read();
44 prop.write = property.write();
45 prop.notify = property.notify();
46
47 if (prop.name.isEmpty() || prop.typeName.isEmpty()) {
48 addError(message: readerParseErrors()
49 .warning(message: tr(sourceText: "Property object is missing a name or type script binding."))
50 .handle());
51 return;
52 }
53 objs[revision].addPropertyDef(propertyDef: prop, option: AddOption::KeepExisting);
54}
55
56void QmltypesReader::insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod,
57 QMap<int, QmlObject> &objs)
58{
59 MethodInfo methodInfo;
60 // ### confusion between Method and Slot. Method should be removed.
61 switch (metaMethod.methodType()) {
62 case QQmlJSMetaMethodType::Method:
63 case QQmlJSMetaMethodType::Slot:
64 methodInfo.methodType = MethodInfo::MethodType::Method;
65 break;
66 case QQmlJSMetaMethodType::Signal:
67 methodInfo.methodType = MethodInfo::MethodType::Signal;
68 break;
69 default:
70 Q_UNREACHABLE();
71 }
72 auto parameters = metaMethod.parameters();
73 qsizetype nParam = parameters.size();
74 for (int i = 0; i < nParam; ++i) {
75 MethodParameter param;
76 param.name = parameters[i].name();
77 param.typeName = parameters[i].typeName();
78 methodInfo.parameters.append(t: param);
79 }
80 methodInfo.name = metaMethod.methodName();
81 methodInfo.typeName = metaMethod.returnTypeName();
82 int revision = metaMethod.revision();
83 methodInfo.isConstructor = metaMethod.isConstructor();
84 if (methodInfo.name.isEmpty()) {
85 addError(message: readerParseErrors().error(message: tr(sourceText: "Method or signal is missing a name.")).handle());
86 return;
87 }
88
89 objs[revision].addMethod(functionDef: methodInfo, option: AddOption::KeepExisting);
90}
91
92EnumDecl QmltypesReader::enumFromMetaEnum(const QQmlJSMetaEnum &metaEnum)
93{
94 EnumDecl res;
95 res.setName(metaEnum.name());
96 res.setAlias(metaEnum.alias());
97 res.setIsFlag(metaEnum.isFlag());
98 QList<EnumItem> values;
99 int lastValue = -1;
100 for (const auto &k : metaEnum.keys()) {
101 if (metaEnum.hasValues())
102 lastValue = metaEnum.value(key: k);
103 else
104 ++lastValue;
105 values.append(t: EnumItem(k, lastValue));
106 }
107 res.setValues(values);
108 return res;
109}
110
111void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
112 const QList<QQmlJSScope::Export> &exportsList)
113{
114 QmltypesComponent comp;
115 QMap<int, QmlObject> objects;
116 {
117 bool hasExports = false;
118 for (const QQmlJSScope::Export &jsE : exportsList) {
119 int metaRev = jsE.version().toEncodedVersion<int>();
120 hasExports = true;
121 objects.insert(key: metaRev, value: QmlObject());
122 }
123 if (!hasExports)
124 objects.insert(key: 0, value: QmlObject());
125 }
126 bool incrementedPath = false;
127 QString prototype;
128 QString defaultPropertyName;
129 {
130 QHash<QString, QQmlJSMetaProperty> els = jsScope->ownProperties();
131 auto it = els.cbegin();
132 auto end = els.cend();
133 while (it != end) {
134 insertProperty(jsScope, property: it.value(), objs&: objects);
135 ++it;
136 }
137 }
138 {
139 QMultiHash<QString, QQmlJSMetaMethod> els = jsScope->ownMethods();
140 auto it = els.cbegin();
141 auto end = els.cend();
142 while (it != end) {
143 insertSignalOrMethod(metaMethod: it.value(), objs&: objects);
144 ++it;
145 }
146 }
147 {
148 QHash<QString, QQmlJSMetaEnum> els = jsScope->ownEnumerations();
149 auto it = els.cbegin();
150 auto end = els.cend();
151 while (it != end) {
152 comp.addEnumeration(enumeration: enumFromMetaEnum(metaEnum: it.value()));
153 ++it;
154 }
155 }
156 comp.setFileName(jsScope->filePath());
157 comp.setName(jsScope->internalName());
158 m_currentPath = m_currentPath.key(name: comp.name())
159 .index(i: qmltypesFilePtr()->components().values(key: comp.name()).size());
160 incrementedPath = true;
161 prototype = jsScope->baseTypeName();
162 defaultPropertyName = jsScope->ownDefaultPropertyName();
163 comp.setInterfaceNames(jsScope->interfaceNames());
164 QString typeName = jsScope->ownAttachedTypeName();
165 comp.setAttachedTypeName(typeName);
166 if (!typeName.isEmpty())
167 comp.setAttachedTypePath(Paths::lookupCppTypePath(name: typeName));
168 comp.setIsSingleton(jsScope->isSingleton());
169 comp.setIsCreatable(jsScope->isCreatable());
170 comp.setIsComposite(jsScope->isComposite());
171 comp.setHasCustomParser(jsScope->hasCustomParser());
172 comp.setValueTypeName(jsScope->valueTypeName());
173 comp.setAccessSemantics(jsScope->accessSemantics());
174 comp.setExtensionTypeName(jsScope->extensionTypeName());
175 comp.setExtensionIsNamespace(jsScope->extensionIsNamespace());
176 Path exportSourcePath = qmltypesFile().canonicalPath();
177 QMap<int, Path> revToPath;
178 auto it = objects.end();
179 auto begin = objects.begin();
180 int objectIndex = 0;
181 QList<int> metaRevs;
182 Path compPath = qmltypesFile()
183 .canonicalPath()
184 .field(name: Fields::components)
185 .key(name: comp.name())
186 .index(i: qmltypesFilePtr()->components().values(key: comp.name()).size());
187
188 // emit & map objs
189 while (it != begin) {
190 --it;
191 if (it.key() < 0) {
192 addError(message: readerParseErrors().error(
193 message: tr(sourceText: "negative meta revision %1 not supported").arg(a: it.key())));
194 }
195 revToPath.insert(key: it.key(), value: compPath.field(name: Fields::objects).index(i: objectIndex));
196 Path nextObjectPath = compPath.field(name: Fields::objects).index(i: ++objectIndex);
197 if (it == begin) {
198 if (!prototype.isEmpty())
199 it->addPrototypePath(prototypePath: Paths::lookupCppTypePath(name: prototype));
200 it->setName(prototype);
201 } else {
202 it->addPrototypePath(prototypePath: nextObjectPath);
203 it->setName(comp.name() + QLatin1String("-") + QString::number(it.key()));
204 }
205 comp.addObject(object: *it);
206 metaRevs.append(t: it.key());
207 }
208 comp.setMetaRevisions(metaRevs);
209
210 // exports:
211 QList<Export> exports;
212 for (const QQmlJSScope::Export &jsE : exportsList) {
213 auto v = jsE.version();
214 int metaRev = v.toEncodedVersion<int>();
215 Export e;
216 e.uri = jsE.package();
217 e.typeName = jsE.type();
218 e.version = Version((v.hasMajorVersion() ? v.majorVersion() : Version::Latest),
219 (v.hasMinorVersion() ? v.minorVersion() : Version::Latest));
220 e.typePath = revToPath.value(key: metaRev);
221 if (!e.typePath) {
222 qCWarning(domLog) << "could not find version" << metaRev << "in" << revToPath.keys();
223 }
224 e.exportSourcePath = exportSourcePath;
225 comp.addExport(exportedEntry: e);
226 }
227
228 if (comp.name().isEmpty()) {
229 addError(message: readerParseErrors()
230 .error(message: tr(sourceText: "Component definition is missing a name binding."))
231 .handle());
232 return;
233 }
234 qmltypesFilePtr()->addComponent(comp, option: AddOption::KeepExisting);
235 if (incrementedPath)
236 m_currentPath = m_currentPath.dropTail().dropTail();
237}
238
239bool QmltypesReader::parse()
240{
241 QQmlJSTypeDescriptionReader reader(qmltypesFilePtr()->canonicalFilePath(),
242 qmltypesFilePtr()->code());
243 QStringList dependencies;
244 QList<QQmlJSExportedScope> objects;
245 m_isValid = reader(&objects, &dependencies);
246 for (const auto &obj : std::as_const(t&: objects))
247 insertComponent(jsScope: obj.scope, exportsList: obj.exports);
248 qmltypesFilePtr()->setIsValid(m_isValid);
249 return m_isValid;
250}
251
252void QmltypesReader::addError(ErrorMessage message)
253{
254 if (message.file.isEmpty())
255 message.file = qmltypesFile().canonicalFilePath();
256 if (!message.path)
257 message.path = m_currentPath;
258 qmltypesFilePtr()->addErrorLocal(msg: message.handle());
259}
260
261} // end namespace Dom
262} // end namespace QQmlJS
263QT_END_NAMESPACE
264

source code of qtdeclarative/src/qmldom/qqmldomtypesreader.cpp