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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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