Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmltypesclassdescription.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
6
10
11#include <QtCore/qcborarray.h>
12#include <QtCore/qcbormap.h>
14
15using namespace Qt::StringLiterals;
16using namespace Constants;
17using namespace Constants::MetatypesDotJson;
19using namespace QAnyStringViewUtils;
20
21template<typename Container>
22static void collectExtraVersions(const Container &items, QList<QTypeRevision> &extraVersions)
23{
24 for (const auto &obj : items) {
25 if (obj.revision.isValid() && !extraVersions.contains(obj.revision))
26 extraVersions.append(obj.revision);
27 }
28}
29
30struct Compare {
31 bool operator()(const QAnyStringView &typeName, const MetaType &type) const
32 {
33 return typeName < type.qualifiedClassName();
34 }
35
36 bool operator()(const MetaType &type, const QAnyStringView &typeName) const
37 {
38 return type.qualifiedClassName() < typeName;
39 }
40};
41
43{
44 if (single.inputFile().isEmpty()) {
45 javaScript = single;
46 javaScriptOrigin = origin;
47 } else {
48 native = single;
49 nativeOrigin = origin;
50 }
51}
52
54{
55 if (category.inputFile().isEmpty()) {
56 if (javaScript.isEmpty()) {
58 << relation << "type of" << category.qualifiedClassName()
59 << "is not a JavaScript type";
60 }
61 return javaScript;
62 }
63
64 if (native.isEmpty()) {
66 << relation << "of" << category.qualifiedClassName()
67 << "is not a native type";
68 }
69 return native;
70}
71
73 const QVector<MetaType> &types, const QVector<MetaType> &foreign,
74 const QAnyStringView &name, const QList<QAnyStringView> &namespaces)
75{
76 const auto tryFindType = [&](QAnyStringView qualifiedName) -> FoundType {
78 for (const QVector<MetaType> &t : {types, foreign}) {
79 const auto [first, last] = std::equal_range(
80 t.begin(), t.end(), qualifiedName, Compare());
81 for (auto it = first; it != last; ++it) {
82 Q_ASSERT(it->qualifiedClassName() == qualifiedName);
83
84 if (it->inputFile().isEmpty()) {
85 if (result.javaScript.isEmpty()) {
86 result.javaScript = *it;
87 result.javaScriptOrigin = (&t == &types)
90 } else {
91 warning(result.javaScript)
92 << "Multiple JavaScript types called" << qualifiedName << "found!";
93 }
94 } else if (result.native.isEmpty()) {
95 result.native = *it;
96 result.nativeOrigin = (&t == &types)
99 } else {
100 warning(result.native)
101 << "Multiple C++ types called" << qualifiedName << "found!"
102 << "This violates the One Definition Rule!";
103 }
104 }
105 }
106
107 return result;
108 };
109
110 if (startsWith(name, QLatin1String("::")))
111 return tryFindType(name.mid(2));
112
113 QString qualified;
114 for (int i = 0, end = namespaces.length(); i != end; ++i) {
115 for (int j = 0; j < end - i; ++j) {
116 namespaces[j].visit([&](auto data) { qualified.append(data); });
117 qualified.append(QLatin1String("::"));
118 }
119 name.visit([&](auto data) { qualified.append(data); });
120 if (const FoundType found = tryFindType(qualified))
121 return found;
122
123 qualified.truncate(0);
124 }
125
126 return tryFindType(name);
127}
128
129void QmlTypesClassDescription::collectSuperClasses(
130 const MetaType &classDef, const QVector<MetaType> &types,
131 const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision)
132{
133 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
134 for (const BaseType &superObject : std::as_const(classDef.superClasses())) {
135 if (superObject.access == Access::Public) {
136 const QAnyStringView superName = superObject.name;
137
138 const CollectMode superMode = (mode == TopLevel) ? SuperClass : RelatedType;
139 if (const FoundType found = findType(types, foreign, superName, namespaces)) {
140 const MetaType other = found.select(classDef, "Base");
141 collect(other, types, foreign, superMode, defaultRevision);
142 if (mode == TopLevel && superClass.isEmpty())
143 superClass = other.qualifiedClassName();
144 }
145
146 // If we cannot locate a type for it, there is no point in recording the superClass
147 }
148 }
149}
150
151void QmlTypesClassDescription::collectInterfaces(const MetaType &classDef)
152{
153 for (const Interface &iface : classDef.ifaces())
155}
156
158 const MetaType &classDef, const QVector<MetaType> &types,
159 const QVector<MetaType> &foreign, QTypeRevision defaultRevision)
160{
161 file = classDef.inputFile();
162
163 resolvedClass = classDef;
164 className = classDef.qualifiedClassName();
165
166 switch (classDef.kind()) {
167 case MetaType::Kind::Object:
169 break;
170 case MetaType::Kind::Gadget:
172 break;
173 case MetaType::Kind::Namespace:
174 case MetaType::Kind::Unknown:
176 break;
177 }
178
179 for (const ClassInfo &obj : classDef.classInfos()) {
180 if (obj.name == S_DEFAULT_PROPERTY)
181 defaultProp = obj.value;
182 else if (obj.name == S_PARENT_PROPERTY)
183 parentProp = obj.value;
184 else if (obj.name == S_REGISTER_ENUM_CLASSES_UNSCOPED && obj.value == S_FALSE)
186 }
187
188 collectInterfaces(classDef);
189 collectSuperClasses(classDef, types, foreign, TopLevel, defaultRevision);
190}
191
193 const MetaType &classDef, const QVector<MetaType> &types,
194 const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision)
195{
196 if (file.isEmpty())
197 file = classDef.inputFile();
198
199 const QAnyStringView classDefName = classDef.className();
200 const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
201
202 QAnyStringView foreignTypeName;
203 bool foreignIsNamespace = false;
204 bool isConstructible = false;
205 for (const ClassInfo &obj : classDef.classInfos()) {
206 const QAnyStringView name = obj.name;
207 const QAnyStringView value = obj.value;
208
209 if (name == S_DEFAULT_PROPERTY) {
212 continue;
213 }
214
215 if (name == S_PARENT_PROPERTY) {
218 continue;
219 }
220
222 if (mode != RelatedType && value == S_FALSE)
224 continue;
225 }
226
227 if (name == S_ADDED_IN_VERSION) {
228 const QTypeRevision revision = handleInMinorVersion(
230 defaultRevision.majorVersion());
231 revisions.append(revision);
232 if (mode == TopLevel)
233 addedInRevision = revision;
234 continue;
235 }
236
237 if (mode != TopLevel)
238 continue;
239
240 if (name == S_REMOVED_IN_VERSION) {
243 defaultRevision.majorVersion());
244 continue;
245 }
246
247 // These only apply to the original class
248 if (name == S_ELEMENT) {
249 if (value == S_AUTO)
250 elementNames.append(classDefName);
251 else if (value != S_ANONYMOUS)
253 } else if (name == S_CREATABLE) {
255 } else if (name == S_CREATION_METHOD) {
257 isConstructible = isStructured || (value == S_CONSTRUCT);
258 } else if (name == S_ATTACHED) {
259 if (const FoundType attached = collectRelated(
260 value, types, foreign, defaultRevision, namespaces)) {
261 attachedType = attached.select(classDef, "Attached").qualifiedClassName();
262 }
263 } else if (name == S_EXTENDED) {
265 value, types, foreign, defaultRevision, namespaces)) {
266 javaScriptExtensionType = extension.javaScript.qualifiedClassName();
267 nativeExtensionType = extension.native.qualifiedClassName();
268 }
269 } else if (name == S_EXTENSION_IS_JAVA_SCRIPT) {
270 if (value == S_TRUE)
272 } else if (name == S_EXTENSION_IS_NAMESPACE) {
273 if (value == S_TRUE)
275 } else if (name == S_SEQUENCE) {
276 if (const FoundType element = collectRelated(
277 value, types, foreign, defaultRevision, namespaces)) {
278 sequenceValueType = element.select(classDef, "Sequence value").qualifiedClassName();
279 } else {
280 // TODO: get rid of this once we have JSON data for the builtins.
282 }
283 } else if (name == S_SINGLETON) {
284 if (value == S_TRUE)
285 isSingleton = true;
286 } else if (name == S_FOREIGN) {
287 foreignTypeName = value;
288 } else if (name == S_FOREIGN_IS_NAMESPACE) {
289 foreignIsNamespace = (value == S_TRUE);
290 } else if (name == S_PRIMITIVE_ALIAS) {
292 } else if (name == S_ROOT) {
293 isRootClass = (value == S_TRUE);
294 } else if (name == S_HAS_CUSTOM_PARSER) {
295 if (value == S_TRUE)
296 hasCustomParser = true;
297 } else if (name == S_DEFERRED_PROPERTY_NAMES) {
299 } else if (name == S_IMMEDIATE_PROPERTY_NAMES) {
301 }
302 }
303
306
307 // If the local type is a namespace the result can only be a namespace,
308 // no matter what the foreign type is.
309 const bool isNamespace = foreignIsNamespace || classDef.kind() == MetaType::Kind::Namespace;
310
311 MetaType resolved = classDef;
312 if (!foreignTypeName.isEmpty()) {
313 // We can re-use a type with own QML.* macros as target of QML.Foreign
314 if (const FoundType found = findType(foreign, types, foreignTypeName, namespaces)) {
315 resolved = found.select(classDef, "Foreign");
316
317 // Default properties and enum classes are always local.
318 defaultProp = {};
320
321 // Foreign type can have a default property or an attached type,
322 // or RegisterEnumClassesUnscoped classinfo.
323 for (const ClassInfo &obj : resolved.classInfos()) {
324 const QAnyStringView foreignName = obj.name;
325 const QAnyStringView foreignValue = obj.value;
326 if (defaultProp.isEmpty() && foreignName == S_DEFAULT_PROPERTY) {
327 defaultProp = foreignValue;
328 } else if (parentProp.isEmpty() && foreignName == S_PARENT_PROPERTY) {
329 parentProp = foreignValue;
330 } else if (foreignName == S_REGISTER_ENUM_CLASSES_UNSCOPED) {
331 if (foreignValue == S_FALSE)
333 } else if (foreignName == S_ATTACHED) {
334 if (const FoundType attached = collectRelated(
335 foreignValue, types, foreign, defaultRevision, namespaces)) {
336 attachedType = attached.select(resolved, "Attached").qualifiedClassName();
337 }
338 } else if (foreignName == S_EXTENDED) {
340 foreignValue, types, foreign, defaultRevision, namespaces)) {
341 nativeExtensionType = extension.native.qualifiedClassName();
342 javaScriptExtensionType = extension.javaScript.qualifiedClassName();
343 }
344 } else if (foreignName == S_EXTENSION_IS_JAVA_SCRIPT) {
345 if (foreignValue == S_TRUE)
347 } else if (foreignName == S_EXTENSION_IS_NAMESPACE) {
348 if (foreignValue == S_TRUE)
350 } else if (foreignName == S_SEQUENCE) {
351 if (const FoundType element = collectRelated(
352 foreignValue, types, foreign, defaultRevision, namespaces)) {
354 = element.select(resolved, "Sequence value").qualifiedClassName();
355 }
356 }
357 }
358 } else {
359 className = foreignTypeName;
360 resolved = MetaType();
361 }
362 }
363
364 if (!resolved.isEmpty()) {
365 if (mode == RelatedType || !elementNames.isEmpty()) {
369 }
370
371 collectSuperClasses(resolved, types, foreign, mode, defaultRevision);
372 }
373
374 if (mode != TopLevel)
375 return;
376
377 if (!resolved.isEmpty())
378 collectInterfaces(resolved);
379
380 if (!addedInRevision.isValid()) {
381 addedInRevision = defaultRevision;
382 }
383 if (addedInRevision <= defaultRevision
384 && (!removedInRevision.isValid() || defaultRevision < removedInRevision)) {
385 revisions.append(defaultRevision);
386 }
387
388 std::sort(revisions.begin(), revisions.end());
389 const auto end = std::unique(revisions.begin(), revisions.end());
391
392 resolvedClass = resolved;
393 if (className.isEmpty() && !resolved.isEmpty())
394 className = resolved.qualifiedClassName();
395
396 if (!sequenceValueType.isEmpty()) {
397 isCreatable = false;
399 } else if (isNamespace) {
400 isCreatable = false;
402 } else if (resolved.kind() == MetaType::Kind::Object) {
404 } else {
405 isCreatable = isConstructible;
406
407 if (resolved.isEmpty()) {
408 if (elementNames.isEmpty()) {
409 // If no resolved, we generally assume it's a value type defined by the
410 // foreign/extended trick.
412 }
413
414 for (auto elementName = elementNames.begin(); elementName != elementNames.end();) {
415 if (elementName->isEmpty() || elementName->front().isLower()) {
416 // If no resolved, we generally assume it's a value type defined by the
417 // foreign/extended trick.
419 ++elementName;
420 } else {
421 // Objects and namespaces always have metaobjects and therefore classDefs.
422 // However, we may not be able to resolve the metaobject at compile time. See
423 // the "Invisible" test case. In that case, we must not assume anything about
424 // access semantics.
425
426 warning(classDef)
427 << "Refusing to generate non-lowercase name"
428 << *elementName << "for unknown foreign type";
429 elementName = elementNames.erase(elementName);
430
431 if (elementNames.isEmpty()) {
432 // Make it completely inaccessible.
433 // We cannot get enums from anonymous types after all.
435 }
436 }
437 }
438 } else if (resolved.kind() == MetaType::Kind::Gadget) {
440 } else {
442 }
443 }
444}
445
447 QAnyStringView related, const QVector<MetaType> &types, const QVector<MetaType> &foreign,
448 QTypeRevision defaultRevision, const QList<QAnyStringView> &namespaces)
449{
450 if (FoundType other = findType(types, foreign, related, namespaces)) {
451 if (!other.native.isEmpty())
452 collect(other.native, types, foreign, RelatedType, defaultRevision);
453 if (!other.javaScript.isEmpty())
454 collect(other.javaScript, types, foreign, RelatedType, defaultRevision);
455 return other;
456 }
457 return FoundType();
458}
459
460
462 : type(alias)
463{
464 if (type.isEmpty())
465 return;
466
467 if (type == "void") {
468 type = "";
469 return;
470 }
471
472 // This is a best effort approach and will not return correct results in the
473 // presence of typedefs.
474
475 auto handleList = [&](QLatin1StringView list) {
476 if (!startsWith(type, list) || type.back() != '>'_L1)
477 return false;
478
479 const int listSize = list.size();
480 const QAnyStringView elementType = trimmed(type.mid(listSize, type.size() - listSize - 1));
481
482 // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will
483 // produce double pointers. QList is only for value types. We can't handle QLists
484 // of pointers (unless specially registered, but then they're not isList).
485 if (elementType.back() == '*'_L1)
486 return false;
487
488 isList = true;
489 type = elementType;
490 return true;
491 };
492
493 if (!handleList("QQmlListProperty<"_L1) && !handleList("QList<"_L1)) {
494 if (type.back() == '*'_L1) {
495 isPointer = true;
496 type = type.chopped(1);
497 }
498 if (startsWith(type, "const "_L1)) {
499 isConstant = true;
500 type = type.sliced(strlen("const "));
501 }
502 }
503}
504
QAnyStringView qualifiedClassName() const
const Method::Container & sigs() const
const Method::Container & methods() const
const ClassInfo::Container & classInfos() const
const Property::Container & properties() const
static QList< QAnyStringView > namespaces(const MetaType &classDef)
\inmodule QtCore
constexpr QChar back() const
Returns the last character in the string view.
Definition qstring.h:122
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr QAnyStringView chopped(qsizetype n) const
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:889
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
const_iterator constEnd() const noexcept
Definition qlist.h:633
bool isEmpty() const
Definition qset.h:52
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6319
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
constexpr bool isValid() const
Returns true if the major version or the minor version is known, otherwise false.
static constexpr QTypeRevision fromEncodedVersion(Integer value)
Produces a QTypeRevision from the given value.
void extension()
[6]
Definition dialogs.cpp:230
const QLoggingCategory & category()
[1]
QSet< QString >::iterator it
static constexpr QLatin1StringView S_REFERENCE
static constexpr QLatin1StringView S_VALUE
static constexpr QLatin1StringView S_NONE
static constexpr QLatin1StringView S_SEQUENCE
static constexpr QLatin1StringView S_ATTACHED
static constexpr QLatin1StringView S_FOREIGN
static constexpr QLatin1StringView S_SEQUENCE
static constexpr QLatin1StringView S_PRIMITIVE_ALIAS
static constexpr QLatin1StringView S_FOREIGN_IS_NAMESPACE
static constexpr QLatin1StringView S_EXTENDED
static constexpr QLatin1StringView S_SINGLETON
static constexpr QLatin1StringView S_HAS_CUSTOM_PARSER
static constexpr QLatin1StringView S_EXTENSION_IS_NAMESPACE
static constexpr QLatin1StringView S_EXTENSION_IS_JAVA_SCRIPT
static constexpr QLatin1StringView S_REMOVED_IN_VERSION
static constexpr QLatin1StringView S_ELEMENT
static constexpr QLatin1StringView S_CREATION_METHOD
static constexpr QLatin1StringView S_ADDED_IN_VERSION
static constexpr QLatin1StringView S_CREATABLE
static constexpr QLatin1StringView S_ROOT
static constexpr QLatin1StringView S_ANONYMOUS
static constexpr QLatin1StringView S_DEFERRED_PROPERTY_NAMES
static constexpr QLatin1StringView S_STRUCTURED
static constexpr QLatin1StringView S_TRUE
static constexpr QLatin1StringView S_PARENT_PROPERTY
static constexpr QLatin1StringView S_REGISTER_ENUM_CLASSES_UNSCOPED
static constexpr QLatin1StringView S_DEFAULT_PROPERTY
static constexpr QLatin1StringView S_FALSE
static constexpr QLatin1StringView S_IMMEDIATE_PROPERTY_NAMES
static constexpr QLatin1StringView S_CONSTRUCT
static constexpr QLatin1StringView S_AUTO
QAnyStringView trimmed(QAnyStringView string)
bool startsWith(QAnyStringView whole, QLatin1StringView part)
Combined button and popup list for selecting options.
constexpr QBindableInterface iface
Definition qproperty.h:666
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * typeName
GLenum mode
GLuint GLuint end
GLsizei GLenum GLenum * types
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLuint name
GLint first
GLhandleARB obj
[2]
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
QT_BEGIN_NAMESPACE QTypeRevision handleInMinorVersion(QTypeRevision revision, int majorVersion)
QAnyStringView interfaceName(const Interface &iface)
QDebug warning(QAnyStringView fileName, int lineNumber)
static void collectExtraVersions(const Container &items, QList< QTypeRevision > &extraVersions)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static void split(QT_FT_Vector *b)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
static int toInt(const QChar &qc, int R)
QList< int > list
[14]
QSharedPointer< T > other(t)
[5]
QList< QTreeWidgetItem * > items
bool operator()(const QAnyStringView &typeName, const MetaType &type) const
bool operator()(const MetaType &type, const QAnyStringView &typeName) const
MetaType select(const MetaType &category, QAnyStringView relation) const
FoundType()=default
QList< QAnyStringView > implementsInterfaces
QList< QAnyStringView > primitiveAliases
void collect(const MetaType &classDef, const QVector< MetaType > &types, const QVector< MetaType > &foreign, CollectMode mode, QTypeRevision defaultRevision)
QList< QAnyStringView > immediateNames
FoundType collectRelated(QAnyStringView related, const QVector< MetaType > &types, const QVector< MetaType > &foreign, QTypeRevision defaultRevision, const QList< QAnyStringView > &namespaces)
static FoundType findType(const QVector< MetaType > &types, const QVector< MetaType > &foreign, const QAnyStringView &name, const QList< QAnyStringView > &namespaces)
QList< QAnyStringView > elementNames
QList< QAnyStringView > deferredNames
void collectLocalAnonymous(const MetaType &classDef, const QVector< MetaType > &types, const QVector< MetaType > &foreign, QTypeRevision defaultRevision)
ResolvedTypeAlias(QAnyStringView alias)