Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljstyperesolver.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
6
10#include "qqmljsutils_p.h"
11#include <private/qv4value_p.h>
12
13#include <private/qduplicatetracker_p.h>
14
15#include <QtCore/qloggingcategory.h>
16#include <QtCore/qtyperevision.h>
17
19
20using namespace Qt::StringLiterals;
21
22Q_STATIC_LOGGING_CATEGORY(lcTypeResolver, "qt.qml.compiler.typeresolver", QtInfoMsg);
23
24static inline void assertExtension(const QQmlJSScope::ConstPtr &type, QLatin1String extension)
25{
26 Q_ASSERT(type);
27 Q_ASSERT(type->extensionType().scope->internalName() == extension);
28 Q_ASSERT(type->extensionIsJavaScript());
29}
30
31QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
32 : m_pool(std::make_unique<QQmlJSRegisterContentPool>())
33 , m_imports(importer->builtinInternalNames())
34{
35 const QQmlJSImporter::ImportedTypes &builtinTypes = m_imports;
36
37 m_voidType = builtinTypes.type(u"void"_s).scope;
38 assertExtension(m_voidType, "undefined"_L1);
39
40 m_nullType = builtinTypes.type(u"std::nullptr_t"_s).scope;
41 Q_ASSERT(m_nullType);
42
43 m_realType = builtinTypes.type(u"double"_s).scope;
44 assertExtension(m_realType, "Number"_L1);
45
46 m_floatType = builtinTypes.type(u"float"_s).scope;
47 assertExtension(m_floatType, "Number"_L1);
48
49 m_int8Type = builtinTypes.type(u"qint8"_s).scope;
50 assertExtension(m_int8Type, "Number"_L1);
51
52 m_uint8Type = builtinTypes.type(u"quint8"_s).scope;
53 assertExtension(m_uint8Type, "Number"_L1);
54
55 m_int16Type = builtinTypes.type(u"short"_s).scope;
56 assertExtension(m_int16Type, "Number"_L1);
57
58 m_uint16Type = builtinTypes.type(u"ushort"_s).scope;
59 assertExtension(m_uint16Type, "Number"_L1);
60
61 m_int32Type = builtinTypes.type(u"int"_s).scope;
62 assertExtension(m_int32Type, "Number"_L1);
63
64 m_uint32Type = builtinTypes.type(u"uint"_s).scope;
65 assertExtension(m_uint32Type, "Number"_L1);
66
67 m_int64Type = builtinTypes.type(u"qlonglong"_s).scope;
68 Q_ASSERT(m_int64Type);
69
70 m_uint64Type = builtinTypes.type(u"qulonglong"_s).scope;
71 Q_ASSERT(m_uint64Type);
72
73 m_sizeType = builtinTypes.type(u"qsizetype"_s).scope;
74 assertExtension(m_sizeType, "Number"_L1);
75
76 // qsizetype is either a 32bit or a 64bit signed integer. We don't want to special-case it.
77 Q_ASSERT(m_sizeType == m_int32Type || m_sizeType == m_int64Type);
78
79 m_boolType = builtinTypes.type(u"bool"_s).scope;
80 assertExtension(m_boolType, "Boolean"_L1);
81
82 m_stringType = builtinTypes.type(u"QString"_s).scope;
83 assertExtension(m_stringType, "String"_L1);
84
85 m_stringListType = builtinTypes.type(u"QStringList"_s).scope;
86 assertExtension(m_stringListType, "Array"_L1);
87
88 m_byteArrayType = builtinTypes.type(u"QByteArray"_s).scope;
89 assertExtension(m_byteArrayType, "ArrayBuffer"_L1);
90
91 m_urlType = builtinTypes.type(u"QUrl"_s).scope;
92 assertExtension(m_urlType, "URL"_L1);
93
94 m_dateTimeType = builtinTypes.type(u"QDateTime"_s).scope;
95 assertExtension(m_dateTimeType, "Date"_L1);
96
97 m_dateType = builtinTypes.type(u"QDate"_s).scope;
98 Q_ASSERT(m_dateType);
99
100 m_timeType = builtinTypes.type(u"QTime"_s).scope;
101 Q_ASSERT(m_timeType);
102
103 m_regexpType = builtinTypes.type(u"QRegularExpression"_s).scope;
104 Q_ASSERT(m_regexpType);
105
106 m_variantListType = builtinTypes.type(u"QVariantList"_s).scope;
107 assertExtension(m_variantListType, "Array"_L1);
108
109 m_variantMapType = builtinTypes.type(u"QVariantMap"_s).scope;
110 Q_ASSERT(m_variantMapType);
111 m_varType = builtinTypes.type(u"QVariant"_s).scope;
112 Q_ASSERT(m_varType);
113
114 m_qmlPropertyMapType = builtinTypes.type(u"QQmlPropertyMap"_s).scope;
115 Q_ASSERT(m_qmlPropertyMapType);
116
117 m_jsValueType = builtinTypes.type(u"QJSValue"_s).scope;
118 Q_ASSERT(m_jsValueType);
119
120 m_qObjectType = builtinTypes.type(u"QObject"_s).scope;
121 assertExtension(m_qObjectType, "Object"_L1);
122
123 m_qObjectListType = builtinTypes.type(u"QObjectList"_s).scope;
124 assertExtension(m_qObjectListType, "Array"_L1);
125
126 m_qQmlScriptStringType = builtinTypes.type(u"QQmlScriptString"_s).scope;
127 Q_ASSERT(m_qQmlScriptStringType);
128
129 m_functionType = builtinTypes.type(u"function"_s).scope;
130 Q_ASSERT(m_functionType);
131
132 m_numberPrototype = builtinTypes.type(u"NumberPrototype"_s).scope;
133 Q_ASSERT(m_numberPrototype);
134
135 m_arrayPrototype = builtinTypes.type(u"ArrayPrototype"_s).scope;
136 Q_ASSERT(m_arrayPrototype);
137
138 m_listPropertyType = m_qObjectType->listType();
139 Q_ASSERT(m_listPropertyType->internalName() == u"QQmlListProperty<QObject>"_s);
140 Q_ASSERT(m_listPropertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence);
141 Q_ASSERT(m_listPropertyType->elementTypeName() == u"QObject"_s);
142 assertExtension(m_listPropertyType, "Array"_L1);
143
144 QQmlJSScope::Ptr emptyType = QQmlJSScope::create();
145 emptyType->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
146 m_emptyType = emptyType;
147
148 QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
149 jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_s);
150 jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_s);
151 jsPrimitiveType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
152 m_jsPrimitiveType = jsPrimitiveType;
153
154 QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create();
155 metaObjectType->setInternalName(u"const QMetaObject"_s);
156 metaObjectType->setFilePath(u"qmetaobject.h"_s);
157 metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
158 m_metaObjectType = metaObjectType;
159
160 m_jsGlobalObject = importer->jsGlobalObject();
161
162 QQmlJSScope::Ptr forInIteratorPtr = QQmlJSScope::create();
163 forInIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
164 forInIteratorPtr->setFilePath(u"qjslist.h"_s);
165 forInIteratorPtr->setInternalName(u"QJSListForInIterator::Ptr"_s);
166 m_forInIteratorPtr = forInIteratorPtr;
167
168 QQmlJSScope::Ptr forOfIteratorPtr = QQmlJSScope::create();
169 forOfIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
170 forOfIteratorPtr->setFilePath(u"qjslist.h"_s);
171 forOfIteratorPtr->setInternalName(u"QJSListForOfIterator::Ptr"_s);
172 m_forOfIteratorPtr = forOfIteratorPtr;
173
174 // We use this as scope type quite often, and it should always be the same scope type.
175 m_jsGlobalObjectContent = m_pool->createType(
176 m_jsGlobalObject, QQmlJSRegisterContent::InvalidLookupIndex,
177 QQmlJSRegisterContent::ScopeObject);
178}
179
180/*!
181 \internal
182
183 Initializes the type resolver. As part of that initialization, makes \a
184 visitor traverse the program when given.
185*/
186void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program)
187{
188 m_logger = visitor->logger();
189
190 m_objectsById.clear();
191 m_objectsByLocation.clear();
192 m_imports.clear();
193 m_signalHandlers.clear();
194
195 if (program)
196 program->accept(visitor);
197
198 m_objectsById = visitor->addressableScopes();
199 m_objectsByLocation = visitor->scopesBylocation();
200 m_signalHandlers = visitor->signalHandlers();
201 m_imports = visitor->imports();
202 m_seenModuleQualifiers = visitor->seenModuleQualifiers();
203}
204
205QQmlJSScope::ConstPtr QQmlJSTypeResolver::mathObject() const
206{
207 return jsGlobalObject()->property(u"Math"_s).type();
208}
209
210QQmlJSScope::ConstPtr QQmlJSTypeResolver::consoleObject() const
211{
212 return jsGlobalObject()->property(u"console"_s).type();
213}
214
215QQmlJSScope::ConstPtr
216QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
217{
218 // #if required for standalone DOM compilation against Qt 6.2
219 qCDebug(lcTypeResolver()).nospace()
220 << "looking for object at " << location.line() << ':' << location.column();
221 return m_objectsByLocation[location];
222}
223
224QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
225{
226 const QString typeId = QmlIR::IRBuilder::asString(type->typeId);
227 if (!type->typeArgument)
228 return m_imports.type(typeId).scope;
229 if (typeId == u"list"_s) {
230 if (const QQmlJSScope::ConstPtr typeArgument = typeForName(type->typeArgument->toString()))
231 return typeArgument->listType();
232 }
233 return QQmlJSScope::ConstPtr();
234}
235
236QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) const
237{
238 QV4::Value value = QV4::Value::fromReturnedValue(rv);
239 if (value.isUndefined())
240 return voidType();
241
242 if (value.isInt32())
243 return int32Type();
244
245 if (value.isBoolean())
246 return boolType();
247
248 if (value.isDouble())
249 return realType();
250
251 if (value.isNull())
252 return nullType();
253
254 if (value.isEmpty())
255 return emptyType();
256
257 return {};
258}
259
260QQmlJSRegisterContent
261QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, QQmlJSRegisterContent left,
262 QQmlJSRegisterContent right) const
263{
264 Q_ASSERT(left.isValid());
265 Q_ASSERT(right.isValid());
266
267 switch (oper) {
268 case QSOperator::Op::Equal:
269 case QSOperator::Op::NotEqual:
270 case QSOperator::Op::StrictEqual:
271 case QSOperator::Op::StrictNotEqual:
272 case QSOperator::Op::Lt:
273 case QSOperator::Op::Gt:
274 case QSOperator::Op::Ge:
275 case QSOperator::Op::In:
276 case QSOperator::Op::Le:
277 return operationType(boolType());
278 case QSOperator::Op::BitAnd:
279 case QSOperator::Op::BitOr:
280 case QSOperator::Op::BitXor:
281 case QSOperator::Op::LShift:
282 case QSOperator::Op::RShift:
283 return operationType(int32Type());
284 case QSOperator::Op::URShift:
285 return operationType(uint32Type());
286 case QSOperator::Op::Add: {
287 const auto leftContents = left.containedType();
288 const auto rightContents = right.containedType();
289 if (leftContents == stringType() || rightContents == stringType())
290 return operationType(stringType());
291
292 const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
293 if (result == boolType())
294 return operationType(int32Type());
295 if (isNumeric(result))
296 return operationType(realType());
297
298 return operationType(jsPrimitiveType());
299 }
300 case QSOperator::Op::Sub:
301 case QSOperator::Op::Mul:
302 case QSOperator::Op::Exp: {
303 const QQmlJSScope::ConstPtr result = merge(left.containedType(), right.containedType());
304 return operationType(result == boolType() ? int32Type() : realType());
305 }
306 case QSOperator::Op::Div:
307 case QSOperator::Op::Mod:
308 return operationType(realType());
309 case QSOperator::Op::As:
310 return operationType(right.containedType());
311 default:
312 break;
313 }
314
315 return operationType(merge(left.containedType(), right.containedType()));
316}
317
318QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
319 UnaryOperator op, QQmlJSRegisterContent operand) const
320{
321 switch (op) {
322 case UnaryOperator::Not:
323 return operationType(boolType());
324 case UnaryOperator::Complement:
325 return operationType(int32Type());
326 case UnaryOperator::Plus:
327 if (isIntegral(operand))
328 return operationType(operand.containedType());
329 Q_FALLTHROUGH();
330 default:
331 if (operand.containedType() == boolType())
332 return operationType(int32Type());
333 break;
334 }
335
336 return operationType(realType());
337}
338
339bool QQmlJSTypeResolver::isPrimitive(QQmlJSRegisterContent type) const
340{
341 return isPrimitive(type.containedType());
342}
343
344bool QQmlJSTypeResolver::isNumeric(QQmlJSRegisterContent type) const
345{
346 return isNumeric(type.containedType());
347}
348
349bool QQmlJSTypeResolver::isIntegral(QQmlJSRegisterContent type) const
350{
351 return isIntegral(type.containedType());
352}
353
354bool QQmlJSTypeResolver::isIntegral(const QQmlJSScope::ConstPtr &type) const
355{
356 return isSignedInteger(type) || isUnsignedInteger(type);
357}
358
359bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
360{
361 return (isNumeric(type) && type != m_int64Type && type != m_uint64Type)
362 || type == m_boolType || type == m_voidType || type == m_nullType
363 || type == m_stringType || type == m_jsPrimitiveType;
364}
365
366bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
367{
368 if (!type) // should this be a precondition instead?
369 return false;
370
371 if (type->scopeType() == QQmlJSScope::ScopeType::EnumScope)
372 return true;
373
374 if (type == m_realType)
375 return true;
376 if (type == m_floatType)
377 return true;
378 if (type == m_int8Type)
379 return true;
380 if (type == m_uint8Type)
381 return true;
382 if (type == m_int16Type)
383 return true;
384 if (type == m_uint16Type)
385 return true;
386 if (type == m_int32Type)
387 return true;
388 if (type == m_uint32Type)
389 return true;
390 if (type == m_int64Type)
391 return true;
392 if (type == m_uint64Type)
393 return true;
394 // sizetype is covered by one of the above cases (int32 or int64)
395 // booleans are not numeric
396
397 // the number prototype is numeric
398 if (type == m_numberPrototype)
399 return true;
400
401 // and types directly inheriting from it, notably number, are also
402 // numeric
403 if (type->baseType() == m_numberPrototype)
404 return true;
405
406 // it isn't possible (for users) to derive from m_numberPrototpye,
407 // so we know the list above is exhaustive / we don't need to go up
408 // further in the inheritance chain
409
410 return false;
411}
412
413bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const
414{
415 return type == m_int8Type || type == m_int16Type
416 || type == m_int32Type || type == m_int64Type;
417}
418
419bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
420{
421 return type == m_uint8Type || type == m_uint16Type
422 || type == m_uint32Type || type == m_uint64Type;
423}
424
425bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
426{
427 return type == m_uint8Type || type == m_int8Type || type == m_uint16Type || type == m_int16Type
428 || type == m_uint32Type || type == m_int32Type;
429}
430
431QQmlJSScope::ConstPtr QQmlJSTypeResolver::containedTypeForName(const QString &name) const
432{
433 QQmlJSScope::ConstPtr type = typeForName(name);
434
435 if (!type || type->isSingleton() || type->isScript())
436 return type;
437
438 switch (type->accessSemantics()) {
439 case QQmlJSScope::AccessSemantics::Reference:
440 if (const auto attached = type->attachedType())
441 return genericType(attached) ? attached : QQmlJSScope::ConstPtr();
442 return metaObjectType();
443 case QQmlJSScope::AccessSemantics::None:
444 return metaObjectType();
445 case QQmlJSScope::AccessSemantics::Sequence:
446 case QQmlJSScope::AccessSemantics::Value:
447 return canAddressValueTypes() ? metaObjectType() : QQmlJSScope::ConstPtr();
448 }
449
450 Q_UNREACHABLE_RETURN(QQmlJSScope::ConstPtr());
451}
452
453QQmlJSRegisterContent QQmlJSTypeResolver::registerContentForName(
454 const QString &name, QQmlJSRegisterContent scopeType) const
455{
456 QQmlJSScope::ConstPtr type = typeForName(name);
457 if (!type)
458 return QQmlJSRegisterContent();
459
460 if (type->isSingleton()) {
461 return m_pool->createType(
462 type, QQmlJSRegisterContent::InvalidLookupIndex,
463 QQmlJSRegisterContent::Singleton, scopeType);
464 }
465
466 if (type->isScript()) {
467 return m_pool->createType(
468 type, QQmlJSRegisterContent::InvalidLookupIndex,
469 QQmlJSRegisterContent::Script, scopeType);
470 }
471
472 const QQmlJSRegisterContent namedType = m_pool->createType(
473 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::TypeByName,
474 scopeType);
475
476 if (const auto attached = type->attachedType()) {
477 if (!genericType(attached)) {
478 m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg(
479 attached->internalName()),
480 qmlCompiler, attached->sourceLocation());
481 return {};
482 } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
483 m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg(
484 type->internalName()),
485 qmlCompiler, type->sourceLocation());
486 return {};
487 } else {
488 // We don't know yet whether we need the attached or the plain object. In direct
489 // mode, we will figure this out using the scope type and access any enums of the
490 // plain type directly. In indirect mode, we can use enum lookups.
491 return m_pool->createType(
492 attached, QQmlJSRegisterContent::InvalidLookupIndex,
493 QQmlJSRegisterContent::Attachment, namedType);
494 }
495 }
496
497 switch (type->accessSemantics()) {
498 case QQmlJSScope::AccessSemantics::None:
499 case QQmlJSScope::AccessSemantics::Reference:
500 // A plain reference to a non-singleton, non-attached type.
501 // We may still need the plain type reference for enum lookups,
502 // Store it as QMetaObject.
503 // This only works with namespaces and object types.
504 return m_pool->createType(
505 metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
506 QQmlJSRegisterContent::MetaType, namedType);
507 case QQmlJSScope::AccessSemantics::Sequence:
508 case QQmlJSScope::AccessSemantics::Value:
509 if (scopeType.isImportNamespace() || canAddressValueTypes()) {
510 return m_pool->createType(
511 metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
512 QQmlJSRegisterContent::MetaType, namedType);
513 }
514 // Else this is not actually a type reference. You cannot get the metaobject
515 // of a value type in QML and sequences don't even have metaobjects.
516 break;
517 }
518
519 return QQmlJSRegisterContent();
520}
521
522QQmlJSRegisterContent QQmlJSTypeResolver::original(QQmlJSRegisterContent type) const
523{
524 QQmlJSRegisterContent result = type.original();
525 return result.isNull() ? type : result;
526}
527
528QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
529 QQmlJSRegisterContent container) const
530{
531 return original(container).containedType();
532}
533
534QQmlJSRegisterContent QQmlJSTypeResolver::shadowed(QQmlJSRegisterContent type) const
535{
536 QQmlJSRegisterContent result = type.shadowed();
537 return result.isNull() ? type : result;
538}
539
540bool QQmlJSTypeResolver::adjustTrackedType(
541 QQmlJSRegisterContent tracked, const QQmlJSScope::ConstPtr &conversion) const
542{
543 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
544 return true;
545
546 QQmlJSScope::ConstPtr contained = tracked.containedType();
547 QQmlJSScope::ConstPtr result = conversion;
548
549 // Do not adjust to the JavaScript extension of the original type. Rather keep the original
550 // type in that case.
551 QQmlJSUtils::searchBaseAndExtensionTypes(
552 contained, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
553 if (kind != QQmlJSScope::ExtensionJavaScript || scope != result)
554 return false;
555 result = contained;
556 return true;
557 });
558
559
560 // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
561 // we better not change the type.
562 if (!canPrimitivelyConvertFromTo(contained, result)
563 && !canPopulate(result, contained, nullptr)
564 && !selectConstructor(result, contained, nullptr).isValid()) {
565 return false;
566 }
567
568 m_pool->adjustType(tracked, result);
569 return true;
570}
571
572bool QQmlJSTypeResolver::adjustTrackedType(
573 QQmlJSRegisterContent tracked, QQmlJSRegisterContent conversion) const
574{
575 return adjustTrackedType(tracked, conversion.containedType());
576}
577
578bool QQmlJSTypeResolver::adjustTrackedType(
579 QQmlJSRegisterContent tracked, const QList<QQmlJSRegisterContent> &conversions) const
580{
581 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
582 return true;
583
584 QQmlJSScope::ConstPtr result;
585 for (QQmlJSRegisterContent type : conversions)
586 result = merge(type.containedType(), result);
587
588 return adjustTrackedType(tracked, result);
589}
590
591void QQmlJSTypeResolver::adjustOriginalType(
592 QQmlJSRegisterContent tracked, const QQmlJSScope::ConstPtr &conversion) const
593{
594 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
595 return;
596
597 m_pool->generalizeType(tracked, conversion);
598}
599
600void QQmlJSTypeResolver::generalizeType(QQmlJSRegisterContent type) const
601{
602 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
603 return;
604
605 for (QQmlJSRegisterContent orig = type; !orig.isNull(); orig = orig.original()) {
606 if (!orig.shadowed().isValid())
607 m_pool->generalizeType(orig, genericType(orig.containedType()));
608 }
609}
610
611bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
612 const QQmlJSScope::ConstPtr &to) const
613{
614 if (canPrimitivelyConvertFromTo(from, to)
615 || canPopulate(to, from, nullptr)
616 || selectConstructor(to, from, nullptr).isValid()) {
617 return true;
618 }
619
620 // ### need a generic solution for custom cpp types:
621 // if (from->m_hasBoolOverload && equals(to, boolType))
622 // return true;
623
624 // All of these types have QString conversions that require a certain format
625 // TODO: Actually verify these strings or deprecate them.
626 // Some of those type are builtins or should be builtins. We should add code for them
627 // in QQmlJSCodeGenerator::conversion().
628 if (from == m_stringType && !to.isNull()) {
629 const QString toTypeName = to->internalName();
630 if (toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
631 || toTypeName == u"QSize"_s || toTypeName == u"QSizeF"_s
632 || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s) {
633 return true;
634 }
635 }
636
637 return false;
638}
639
640bool QQmlJSTypeResolver::canConvertFromTo(QQmlJSRegisterContent from,
641 QQmlJSRegisterContent to) const
642{
643 return canConvertFromTo(from.containedType(), to.containedType());
644}
645
646static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent::ContentVariant a,
647 QQmlJSRegisterContent::ContentVariant b)
648{
649 return (a == b) ? a : QQmlJSRegisterContent::Unknown;
650}
651
652QQmlJSRegisterContent QQmlJSTypeResolver::merge(
653 QQmlJSRegisterContent a, QQmlJSRegisterContent b) const
654{
655 Q_ASSERT(a != b);
656
657 // We cannot easily provide an operator< for QQmlJSRegisterContent.
658 // Therefore we use qHash and operator== here to deduplicate. That's somewhat inefficient.
659 QSet<QQmlJSRegisterContent> origins;
660
661 QQmlJSRegisterContent aResultScope;
662 if (a.isConversion()) {
663 const auto &aOrigins = a.conversionOrigins();
664 for (const auto &aOrigin : aOrigins)
665 origins.insert(aOrigin);
666 aResultScope = a.conversionResultScope();
667 } else {
668 origins.insert(a);
669 aResultScope = a.scope();
670 }
671
672 QQmlJSRegisterContent bResultScope;
673 if (b.isConversion()) {
674 const auto &bOrigins = b.conversionOrigins();
675 for (const auto &bOrigin : bOrigins)
676 origins.insert(bOrigin);
677 bResultScope = b.conversionResultScope();
678 } else {
679 origins.insert(b);
680 bResultScope = b.scope();
681 }
682
683 const auto mergeScopes = [&](QQmlJSRegisterContent a, QQmlJSRegisterContent b) {
684 // If they are the same, we don't want to re-track them.
685 // We want the repetition on type mismatches to converge.
686 return (a == b) ? a : merge(a, b);
687 };
688
689 return m_pool->createConversion(
690 origins.values(),
691 merge(a.containedType(), b.containedType()),
692 mergeScopes(aResultScope, bResultScope),
693 mergeVariants(a.variant(), b.variant()),
694 mergeScopes(a.scope(), b.scope()));
695}
696
697QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
698 const QQmlJSScope::ConstPtr &b) const
699{
700 if (a.isNull())
701 return b;
702
703 if (b.isNull())
704 return a;
705
706 const auto baseOrExtension
707 = [](const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
708 QQmlJSScope::ConstPtr found;
709 QQmlJSUtils::searchBaseAndExtensionTypes(
710 a, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
711 switch (kind) {
712 case QQmlJSScope::NotExtension:
713 // If b inherits scope, then scope is a common base type of a and b
714 if (b->inherits(scope)) {
715 found = scope;
716 return true;
717 }
718 break;
719 case QQmlJSScope::ExtensionJavaScript:
720 // Merging a type with its JavaScript extension produces the type.
721 // Giving the JavaScript extension as type to be read means we expect any type
722 // that fulfills the given JavaScript interface
723 if (scope == b) {
724 found = a;
725 return true;
726 }
727 break;
728 case QQmlJSScope::ExtensionType:
729 case QQmlJSScope::ExtensionNamespace:
730 break;
731 }
732 return false;
733 });
734 return found;
735 };
736
737 if (a == b)
738 return a;
739
740 if (a == jsValueType() || a == varType())
741 return a;
742 if (b == jsValueType() || b == varType())
743 return b;
744
745 const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
746 return (isIntegral(type)
747 && type != uint32Type()
748 && type != int64Type()
749 && type != uint64Type())
750 || type == boolType();
751 };
752
753 if (isInt32Compatible(a) && isInt32Compatible(b))
754 return int32Type();
755
756 const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
757 return (isUnsignedInteger(type) && type != uint64Type()) || type == boolType();
758 };
759
760 if (isUInt32Compatible(a) && isUInt32Compatible(b))
761 return uint32Type();
762
763 if (isNumeric(a) && isNumeric(b))
764 return realType();
765
766 if (isPrimitive(a) && isPrimitive(b))
767 return jsPrimitiveType();
768
769 if (const auto base = baseOrExtension(a, b))
770 return base;
771
772 if (const auto base = baseOrExtension(b, a))
773 return base;
774
775 if ((a == nullType() || a == boolType()) && b->isReferenceType())
776 return b;
777
778 if ((b == nullType() || b == boolType()) && a->isReferenceType())
779 return a;
780
781 return varType();
782}
783
784bool QQmlJSTypeResolver::canHold(
785 const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
786{
787 if (container == contained || container == m_varType || container == m_jsValueType)
788 return true;
789
790 if (container == m_jsPrimitiveType)
791 return isPrimitive(contained);
792
793 if (container == m_variantListType)
794 return contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
795
796 if (container == m_qObjectListType || container == m_listPropertyType) {
797 if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
798 return false;
799 if (QQmlJSScope::ConstPtr element = contained->elementType())
800 return element->isReferenceType();
801 return false;
802 }
803
804 if (QQmlJSUtils::searchBaseAndExtensionTypes(
805 container, [&](const QQmlJSScope::ConstPtr &base) {
806 return base == contained;
807 })) {
808 return true;
809 }
810
811 if (container->isReferenceType()) {
812 if (QQmlJSUtils::searchBaseAndExtensionTypes(
813 contained, [&](const QQmlJSScope::ConstPtr &base) {
814 return base == container;
815 })) {
816 return true;
817 }
818 }
819
820 return false;
821}
822
823
824bool QQmlJSTypeResolver::canHoldUndefined(QQmlJSRegisterContent content) const
825{
826 const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
827 return type == m_voidType || type == m_varType
828 || type == m_jsValueType || type == m_jsPrimitiveType;
829 };
830
831 if (!canBeUndefined(content.containedType()))
832 return false;
833
834 if (!content.isConversion())
835 return true;
836
837 const auto &origins = content.conversionOrigins();
838 for (const auto &origin : origins) {
839 if (canBeUndefined(originalContainedType(origin)))
840 return true;
841 }
842
843 return false;
844}
845
846bool QQmlJSTypeResolver::isOptionalType(QQmlJSRegisterContent content) const
847{
848 if (!content.isConversion())
849 return false;
850
851 const auto &origins = content.conversionOrigins();
852 if (origins.length() != 2)
853 return false;
854
855 // Conversion origins are always adjusted to the conversion result. None of them will be void.
856 // Therefore, retrieve the originals first.
857
858 return original(origins[0]).contains(m_voidType) || original(origins[1]).contains(m_voidType);
859}
860
861QQmlJSRegisterContent QQmlJSTypeResolver::extractNonVoidFromOptionalType(
862 QQmlJSRegisterContent content) const
863{
864 if (!isOptionalType(content))
865 return QQmlJSRegisterContent();
866
867 // Conversion origins are always adjusted to the conversion result. None of them will be void.
868 // Therefore, retrieve the originals first.
869
870 QList<QQmlJSRegisterContent> origins(content.conversionOrigins());
871 std::transform(origins.cbegin(), origins.cend(), origins.begin(),
872 [this](QQmlJSRegisterContent content) {return original(content);});
873 const auto &constOrigins = origins;
874 const QQmlJSRegisterContent result = constOrigins[0].contains(m_voidType)
875 ? constOrigins[1]
876 : constOrigins[0];
877
878 // The result may still be undefined. You can write "undefined ?? undefined ?? 1"
879 return result;
880}
881
882QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
883 const QQmlJSScope::ConstPtr &type,
884 ComponentIsGeneric allowComponent) const
885{
886 if (type->isScript())
887 return m_jsValueType;
888
889 if (type == m_metaObjectType)
890 return m_metaObjectType;
891
892 if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
893 QString unresolvedBaseTypeName;
894 for (auto base = type; base;) {
895 // QObject and QQmlComponent are the two required base types.
896 // Any QML type system has to define those, or use the ones from builtins.
897 // As QQmlComponent is derived from QObject, we can restrict ourselves to the latter.
898 // This results in less if'ery when retrieving a QObject* from somewhere and deciding
899 // what it is.
900 if (base->internalName() == u"QObject"_s) {
901 return base;
902 } else if (allowComponent == ComponentIsGeneric::Yes
903 && base->internalName() == u"QQmlComponent"_s) {
904 return base;
905 }
906
907 if (auto baseBase = base->baseType()) {
908 base = baseBase;
909 } else {
910 unresolvedBaseTypeName = base->baseTypeName();
911 break;
912 }
913 }
914
915 // Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
916 // We don't want to deal with those, but m_jsValueType is the best generic option.
917 if (type->filePath().isEmpty())
918 return m_jsValueType;
919
920 m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent. "
921 "You may need to fully qualify all names in C++ so that moc can see them. "
922 "You may also need to add qt_extract_metatypes(<target containing %2>)."_s
923 .arg(type->internalName(), unresolvedBaseTypeName),
924 qmlCompiler, type->sourceLocation());
925
926 // If it does have a filePath, it's some C++ type which we haven't fully resolved.
927 return m_jsValueType;
928 }
929
930 if (type->isListProperty())
931 return m_listPropertyType;
932
933 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
934 return type->baseType();
935
936 if (isPrimitive(type)) {
937 // If the filePath is set, the type is storable and we can just return it.
938 if (!type->filePath().isEmpty())
939 return type;
940
941 // If the type is JavaScript's 'number' type, it's not directly storable, but still
942 // primitive. We use C++ 'double' then.
943 if (isNumeric(type))
944 return m_realType;
945
946 // Otherwise we use QJSPrimitiveValue.
947 // TODO: JavaScript's 'string' and 'boolean' could be special-cased here.
948 return m_jsPrimitiveType;
949 }
950
951 for (const QQmlJSScope::ConstPtr &builtin : {
952 m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
953 m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
954 m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
955 m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
956 m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
957 m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
958 if (type == builtin || type == builtin->listType())
959 return type;
960 }
961
962 return m_varType;
963}
964
965/*!
966 * \internal
967 * The type of a JavaScript literal value appearing in script code
968 */
969QQmlJSRegisterContent QQmlJSTypeResolver::literalType(const QQmlJSScope::ConstPtr &type) const
970{
971 return m_pool->createType(
972 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Literal);
973}
974
975/*!
976 * \internal
977 * The type of the result of a JavaScript operation
978 */
979QQmlJSRegisterContent QQmlJSTypeResolver::operationType(const QQmlJSScope::ConstPtr &type) const
980{
981 return m_pool->createType(
982 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Operation);
983}
984
985/*!
986 * \internal
987 * A type named explicitly, for example in "as"-casts or as function annotation.
988 */
989QQmlJSRegisterContent QQmlJSTypeResolver::namedType(const QQmlJSScope::ConstPtr &type) const
990{
991 return m_pool->createType(
992 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::TypeByName);
993}
994
995QQmlJSRegisterContent QQmlJSTypeResolver::syntheticType(const QQmlJSScope::ConstPtr &type) const
996{
997 return m_pool->createType(
998 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Unknown);
999}
1000
1001static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
1002 bool isMethod)
1003{
1004 switch (mode) {
1005 case QQmlJSScope::NotExtension:
1006 case QQmlJSScope::ExtensionType:
1007 case QQmlJSScope::ExtensionJavaScript:
1008 return isMethod
1009 ? QQmlJSRegisterContent::Method
1010 : QQmlJSRegisterContent::Property;
1011 case QQmlJSScope::ExtensionNamespace:
1012 break;
1013 }
1014 Q_UNREACHABLE_RETURN(QQmlJSRegisterContent::Unknown);
1015}
1016
1017static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
1018{
1019 Q_ASSERT(scope->isComposite());
1020 const QTypeRevision revision = QTypeRevision::fromEncodedVersion(memberRevision);
1021
1022 // If the memberRevision is either invalid or 0.0, then everything is allowed.
1023 if (!revision.isValid() || revision == QTypeRevision::zero())
1024 return true;
1025
1026 const QTypeRevision typeRevision = QQmlJSScope::nonCompositeBaseRevision(
1027 {scope->baseType(), scope->baseTypeRevision()});
1028
1029 // If the revision is not valid, we haven't found a non-composite base type.
1030 // There is nothing we can say about the property then.
1031 return typeRevision.isValid() && typeRevision >= revision;
1032}
1033
1034QQmlJSScope::ConstPtr QQmlJSTypeResolver::resolveParentProperty(
1035 const QString &name, const QQmlJSScope::ConstPtr &base,
1036 const QQmlJSScope::ConstPtr &propType) const
1037{
1038 if (m_parentMode != UseDocumentParent || name != base->parentPropertyName())
1039 return propType;
1040
1041 const QQmlJSScope::ConstPtr baseParent = base->parentScope();
1042 if (!baseParent || !baseParent->inherits(propType))
1043 return propType;
1044
1045 const QString defaultPropertyName = baseParent->defaultPropertyName();
1046 if (defaultPropertyName.isEmpty()) // no reason to search for bindings
1047 return propType;
1048
1049 const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
1050 = baseParent->propertyBindings(defaultPropertyName);
1051 for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
1052 if (binding.bindingType() == QQmlSA::BindingType::Object && binding.objectType() == base)
1053 return baseParent;
1054 }
1055
1056 return propType;
1057}
1058
1059/*!
1060 * \internal
1061 *
1062 * Retrieves the type of whatever \a name signifies in the given \a scope.
1063 * \a name can be an ID, a property of the scope, a singleton, an attachment,
1064 * a plain type reference or a JavaScript global.
1065 *
1066 * TODO: The lookup is actually wrong. We cannot really retrieve JavaScript
1067 * globals here because any runtime-inserted context property would
1068 * override them. We still do because we don't have a better solution for
1069 * identifying e.g. the console object, yet.
1070 *
1071 * \a options tells us whether to consider components as bound. If components
1072 * are bound we can retrieve objects identified by ID in outer contexts.
1073 *
1074 * TODO: This is also wrong because we should alternate scopes and contexts when
1075 * traveling the scope/context hierarchy. Currently we have IDs from any
1076 * context override all scope properties if components are considered
1077 * bound. This is mostly because we don't care about outer scopes at all;
1078 * so we cannot determine with certainty whether an ID from a far outer
1079 * context is overridden by a property of a near outer scope. To
1080 * complicate this further, user context properties can also be inserted
1081 * in outer contexts at run time, shadowing names in further removed outer
1082 * scopes and contexts. What we need to do is determine where exactly what
1083 * kind of property can show up and defend against that with additional
1084 * pragmas.
1085 *
1086 * Note: It probably takes at least 3 nested bound components in one document to
1087 * trigger the misbehavior.
1088 */
1089QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopedType(
1090 const QQmlJSScope::ConstPtr &scope, const QString &name,
1091 QQmlJSScopesByIdOptions options) const
1092{
1093 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1094 if (m_objectsById.possibleScopes(name, scope, options, identified)
1095 != QQmlJSScopesById::Success::Yes) {
1096 // Could not determine component boundaries
1097 return {};
1098 }
1099
1100 if (identified.result) {
1101 // Found a definite match
1102 return identified.result;
1103 }
1104
1105 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(scope)) {
1106 QQmlJSScope::ConstPtr result;
1107 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1108 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1109 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1110 return false;
1111
1112 if (found->hasOwnProperty(name)) {
1113 const QQmlJSMetaProperty prop = found->ownProperty(name);
1114 if (!isRevisionAllowed(prop.revision(), scope))
1115 return false;
1116
1117 result = resolveParentProperty(name, base, prop.type());
1118 return true;
1119 }
1120
1121 if (found->hasOwnMethod(name)) {
1122 const auto methods = found->ownMethods(name);
1123 for (const auto &method : methods) {
1124 if (isRevisionAllowed(method.revision(), scope)) {
1125 result = jsValueType();
1126 return true;
1127 }
1128 }
1129 }
1130
1131 return false;
1132 })) {
1133 return result;
1134 }
1135 }
1136
1137 if (QQmlJSScope::ConstPtr result = containedTypeForName(name))
1138 return result;
1139
1140 if (m_jsGlobalObject->hasProperty(name))
1141 return m_jsGlobalObject->property(name).type();
1142
1143 if (m_jsGlobalObject->hasMethod(name))
1144 return jsValueType();
1145
1146 return {};
1147}
1148
1149/*!
1150 * \internal
1151 *
1152 * Same as the other scopedType method, but accepts a QQmlJSRegisterContent and
1153 * also returns one. This way you not only get the type, but also the content
1154 * variant and various meta info.
1155 */
1156QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(QQmlJSRegisterContent scope,
1157 const QString &name, int lookupIndex,
1158 QQmlJSScopesByIdOptions options) const
1159{
1160 const QQmlJSScope::ConstPtr contained = scope.containedType();
1161
1162 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1163 if (m_objectsById.possibleScopes(name, contained, options, identified)
1164 != QQmlJSScopesById::Success::Yes) {
1165 // Could not determine component boundaries
1166 return {};
1167 }
1168
1169 if (identified.result) {
1170 // Found a definite match
1171 return m_pool->createType(
1172 identified.result, lookupIndex, QQmlJSRegisterContent::ObjectById, scope);
1173 }
1174
1175 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(contained)) {
1176 QQmlJSRegisterContent result;
1177 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1178 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1179 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1180 return false;
1181
1182 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1183 ? scope
1184 : extensionType(found, scope);
1185
1186 if (found->hasOwnProperty(name)) {
1187 QQmlJSMetaProperty prop = found->ownProperty(name);
1188 if (!isRevisionAllowed(prop.revision(), contained))
1189 return false;
1190
1191 prop.setType(resolveParentProperty(name, base, prop.type()));
1192 result = m_pool->createProperty(
1193 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1194 scopeContentVariant(mode, false), resultScope);
1195 return true;
1196 }
1197
1198 if (found->hasOwnMethod(name)) {
1199 auto methods = found->ownMethods(name);
1200 for (auto it = methods.begin(); it != methods.end();) {
1201 if (!isRevisionAllowed(it->revision(), contained))
1202 it = methods.erase(it);
1203 else
1204 ++it;
1205 }
1206 if (methods.isEmpty())
1207 return false;
1208 result = m_pool->createMethod(
1209 methods, jsValueType(), scopeContentVariant(mode, true), resultScope);
1210 return true;
1211 }
1212
1213 // Unqualified enums are not allowed
1214 return false;
1215 })) {
1216 return result;
1217 }
1218 }
1219
1220 QQmlJSRegisterContent result = registerContentForName(name, scope);
1221
1222 if (result.isValid())
1223 return result;
1224
1225 if (m_jsGlobalObject->hasProperty(name)) {
1226 return m_pool->createProperty(
1227 m_jsGlobalObject->property(name), QQmlJSRegisterContent::InvalidLookupIndex,
1228 lookupIndex, QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1229 } else if (m_jsGlobalObject->hasMethod(name)) {
1230 return m_pool->createMethod(
1231 m_jsGlobalObject->methods(name), jsValueType(),
1232 QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1233 }
1234
1235 return {};
1236}
1237
1238/*!
1239 * \fn QQmlJSScope::ConstPtr typeForId(const QQmlJSScope::ConstPtr &scope, const QString &name, QQmlJSScopesByIdOptions options) const
1240 *
1241 * \internal
1242 *
1243 * Same as scopedType(), but assumes that the \a name is an ID and only searches
1244 * the context.
1245 *
1246 * TODO: This is just as wrong as scopedType() in that it disregards both scope
1247 * properties overriding context properties and run time context
1248 * properties.
1249 */
1250
1251bool QQmlJSTypeResolver::checkEnums(
1252 QQmlJSRegisterContent scope, const QString &name,
1253 QQmlJSRegisterContent *result) const
1254{
1255 // You can't have lower case enum names in QML, even if we know the enums here.
1256 if (name.isEmpty() || !name.at(0).isUpper())
1257 return false;
1258
1259 const auto enums = scope.containedType()->ownEnumerations();
1260 for (const auto &enumeration : enums) {
1261 if (enumeration.name() == name) {
1262 *result = m_pool->createEnumeration(
1263 enumeration, QString(),
1264 QQmlJSRegisterContent::Enum,
1265 scope);
1266 return true;
1267 }
1268
1269 if (!(scope.containedType()->enforcesScopedEnums() && enumeration.isScoped())
1270 && enumeration.hasKey(name)) {
1271 *result = m_pool->createEnumeration(
1272 enumeration, name,
1273 QQmlJSRegisterContent::Enum,
1274 scope);
1275 return true;
1276 }
1277 }
1278
1279 return false;
1280}
1281
1282bool QQmlJSTypeResolver::canPopulate(
1283 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1284 bool *isExtension) const
1285{
1286 // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
1287 // code in the code generator.
1288
1289 if (type.isNull()
1290 || canHold(passedArgumentType, type)
1291 || isPrimitive(passedArgumentType)
1292 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1293 || !type->isStructured()) {
1294 return false;
1295 }
1296
1297 if (isExtension)
1298 *isExtension = !type->extensionType().scope.isNull();
1299
1300 return true;
1301}
1302
1303QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
1304 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1305 bool *isExtension) const
1306{
1307 // If the "from" type can hold the target type, we should not try to coerce
1308 // it to any constructor argument.
1309 if (type.isNull()
1310 || canHold(passedArgumentType, type)
1311 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1312 || !type->isCreatable()) {
1313 return QQmlJSMetaMethod();
1314 }
1315
1316 auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &ctorProvider) {
1317 QQmlJSMetaMethod candidate;
1318
1319 const auto ownMethods = ctorProvider->ownMethods();
1320 for (const QQmlJSMetaMethod &method : ownMethods) {
1321 if (!method.isConstructor())
1322 continue;
1323
1324 const auto index = method.constructorIndex();
1325 Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
1326
1327 const auto methodArguments = method.parameters();
1328 if (methodArguments.size() != 1)
1329 continue;
1330
1331 const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
1332
1333 // Do not select the copy constructor (also not if disguised via the extension).
1334 if (methodArgumentType == type)
1335 continue;
1336
1337 if (passedArgumentType == methodArgumentType)
1338 return method;
1339
1340 // Do not select further ctors here. We don't want to do multi-step construction as that
1341 // is confusing and easily leads to infinite recursion.
1342 if (!candidate.isValid()
1343 && canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
1344 candidate = method;
1345 }
1346 }
1347
1348 return candidate;
1349 };
1350
1351 if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
1352 const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
1353 if (ctor.isValid()) {
1354 if (isExtension)
1355 *isExtension = true;
1356 return ctor;
1357 }
1358 }
1359
1360 if (isExtension)
1361 *isExtension = false;
1362
1363 return doSelectConstructor(type);
1364}
1365
1366bool QQmlJSTypeResolver::areEquivalentLists(
1367 const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
1368{
1369 const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
1370 { m_stringListType, m_stringType->listType() },
1371 { m_variantListType, m_varType->listType() }
1372 };
1373
1374 for (const auto eq : equivalentLists) {
1375 if ((a == eq[0] && b == eq[1]) || (a == eq[1] && b == eq[0]))
1376 return true;
1377 }
1378
1379 return false;
1380}
1381
1382bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
1383{
1384 // pointers are trivially copyable
1385 if (type->isReferenceType())
1386 return true;
1387
1388 // Enum values are trivially copyable
1389 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
1390 return true;
1391
1392 for (const QQmlJSScope::ConstPtr &trivial : {
1393 m_nullType, m_voidType,
1394 m_boolType, m_metaObjectType,
1395 m_realType, m_floatType,
1396 m_int8Type, m_uint8Type,
1397 m_int16Type, m_uint16Type,
1398 m_int32Type, m_uint32Type,
1399 m_int64Type, m_uint64Type }) {
1400 if (type == trivial)
1401 return true;
1402 }
1403
1404 return false;
1405}
1406
1407bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
1408{
1409 const bool matchByName = !base->isComposite();
1410 for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
1411 derivedBase = derivedBase->baseType()) {
1412 if (derivedBase == base)
1413 return true;
1414 if (matchByName
1415 && !derivedBase->isComposite()
1416 && derivedBase->internalName() == base->internalName()) {
1417 return true;
1418 }
1419 }
1420 return false;
1421}
1422
1423bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
1424 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
1425{
1426 if (from == to)
1427 return true;
1428 if (from == m_varType || to == m_varType)
1429 return true;
1430 if (from == m_jsValueType || to == m_jsValueType)
1431 return true;
1432 if (to == m_qQmlScriptStringType)
1433 return true;
1434 if (isNumeric(from) && isNumeric(to))
1435 return true;
1436 // We can convert everything to bool.
1437 if (to == m_boolType)
1438 return true;
1439
1440 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1441 && to == m_stringType) {
1442 return true;
1443 }
1444
1445 // Yes, our String has number constructors.
1446 if (isNumeric(from) && to == m_stringType)
1447 return true;
1448
1449 // We can convert strings to numbers, but not to enums
1450 if (from == m_stringType && isNumeric(to))
1451 return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
1452
1453 // We can always convert between strings and urls.
1454 if ((from == m_stringType && to == m_urlType)
1455 || (from == m_urlType && to == m_stringType)) {
1456 return true;
1457 }
1458
1459 // We can always convert between strings and byte arrays.
1460 if ((from == m_stringType && to == m_byteArrayType)
1461 || (from == m_byteArrayType && to == m_stringType)) {
1462 return true;
1463 }
1464
1465 if (to == m_voidType)
1466 return true;
1467
1468 if (to.isNull())
1469 return from == m_voidType;
1470
1471 const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
1472 for (const auto &originType : types) {
1473 if (from != originType)
1474 continue;
1475
1476 for (const auto &targetType : types) {
1477 if (to == targetType)
1478 return true;
1479 }
1480
1481 if (to == m_realType)
1482 return true;
1483
1484 break;
1485 }
1486
1487 if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
1488 return true;
1489
1490 if (from == m_jsPrimitiveType) {
1491 // You can cast any primitive to a nullptr
1492 return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
1493 }
1494
1495 if (to == m_jsPrimitiveType)
1496 return isPrimitive(from);
1497
1498 const bool matchByName = !to->isComposite();
1499 Q_ASSERT(!matchByName || !to->internalName().isEmpty());
1500 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1501 from, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
1502 switch (kind) {
1503 case QQmlJSScope::NotExtension:
1504 case QQmlJSScope::ExtensionJavaScript:
1505 // Converting to a base type is trivially supported.
1506 // Converting to the JavaScript extension of a type just produces the type itself.
1507 // Giving the JavaScript extension as type to be converted to means we expect any
1508 // result that fulfills the given JavaScript interface.
1509 return scope == to
1510 || (matchByName && scope->internalName() == to->internalName());
1511 case QQmlJSScope::ExtensionType:
1512 case QQmlJSScope::ExtensionNamespace:
1513 break;
1514 }
1515 return false;
1516 })) {
1517 return true;
1518 }
1519
1520 if (from == m_variantListType)
1521 return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
1522
1523 // We can convert anything that fits natively into QJSPrimitiveValue
1524 if (canPrimitivelyConvertFromTo(from, m_jsPrimitiveType)
1525 && canPrimitivelyConvertFromTo(m_jsPrimitiveType, to)) {
1526 return true;
1527 }
1528
1529 if (areEquivalentLists(from, to))
1530 return true;
1531
1532 if (from->isListProperty()
1533 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1534 && canConvertFromTo(from->elementType(), to->elementType())) {
1535 return true;
1536 }
1537
1538 // it is possible to assing a singlar object to a list property if it could be stored in the list
1539 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1540 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1541 && from->inherits(to->elementType())) {
1542 return true;
1543 }
1544
1545 if (to == m_stringType && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1546 return canConvertFromTo(from->elementType(), m_stringType);
1547
1548 return false;
1549}
1550
1551QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
1552 bool isWritable, QQmlJSRegisterContent scope) const
1553{
1554 QQmlJSMetaProperty prop;
1555 prop.setPropertyName(u"length"_s);
1556 prop.setTypeName(u"qsizetype"_s);
1557 prop.setType(sizeType());
1558 prop.setIsWritable(isWritable);
1559 return m_pool->createProperty(
1560 prop, QQmlJSRegisterContent::InvalidLookupIndex,
1561 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Property, scope);
1562}
1563
1564QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1565 QQmlJSRegisterContent type, const QString &name, int baseLookupIndex,
1566 int resultLookupIndex) const
1567{
1568 QQmlJSRegisterContent result;
1569 const QQmlJSScope::ConstPtr contained = type.containedType();
1570
1571 // If we got a plain type reference we have to check the enums of the _scope_.
1572 if (contained == metaObjectType())
1573 return memberEnumType(type.scope(), name);
1574
1575 if (contained == variantMapType() || contained->inherits(qmlPropertyMapType())) {
1576 QQmlJSMetaProperty prop;
1577 prop.setPropertyName(name);
1578 prop.setTypeName(u"QVariant"_s);
1579 prop.setType(varType());
1580 prop.setIsWritable(true);
1581 return m_pool->createProperty(
1582 prop, baseLookupIndex, resultLookupIndex,
1583 QQmlJSRegisterContent::Property, type);
1584 }
1585
1586 if (contained == jsValueType()) {
1587 QQmlJSMetaProperty prop;
1588 prop.setPropertyName(name);
1589 prop.setTypeName(u"QJSValue"_s);
1590 prop.setType(jsValueType());
1591 prop.setIsWritable(true);
1592 return m_pool->createProperty(
1593 prop, baseLookupIndex, resultLookupIndex,
1594 QQmlJSRegisterContent::Property, type);
1595 }
1596
1597 if ((contained == stringType()
1598 || contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1599 && name == u"length"_s) {
1600 return lengthProperty(contained != stringType(), type);
1601 }
1602
1603 const auto check = [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1604 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1605 ? baseType(scope, type)
1606 : extensionType(scope, type);
1607
1608 if (mode != QQmlJSScope::ExtensionNamespace) {
1609 if (scope->hasOwnProperty(name)) {
1610 const auto prop = scope->ownProperty(name);
1611 result = m_pool->createProperty(
1612 prop, baseLookupIndex, resultLookupIndex,
1613 QQmlJSRegisterContent::Property, resultScope);
1614 return true;
1615 }
1616
1617 if (scope->hasOwnMethod(name)) {
1618 const auto methods = scope->ownMethods(name);
1619 result = m_pool->createMethod(
1620 methods, jsValueType(), QQmlJSRegisterContent::Method, resultScope);
1621 return true;
1622 }
1623 }
1624 // don't look up enums on ids...
1625 if (type.variant() == QQmlJSRegisterContent::ObjectById)
1626 return false;
1627 return checkEnums(resultScope, name, &result);
1628 };
1629
1630 if (QQmlJSUtils::searchBaseAndExtensionTypes(type.containedType(), check))
1631 return result;
1632
1633 for (auto scope = contained;
1634 scope && (QQmlSA::isFunctionScope(scope->scopeType())
1635 || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
1636 scope = scope->parentScope()) {
1637 if (auto ownIdentifier = scope->ownJSIdentifier(name)) {
1638 QQmlJSMetaProperty prop;
1639 prop.setPropertyName(name);
1640 prop.setTypeName(u"QJSValue"_s);
1641 prop.setType(jsValueType());
1642 prop.setIsWritable(!(ownIdentifier.value().isConst));
1643
1644 return m_pool->createProperty(
1645 prop, baseLookupIndex, resultLookupIndex,
1646 QQmlJSRegisterContent::Property,
1647 parentScope(scope, type));
1648 }
1649 }
1650
1651 // check enums before checking attached types of attached types (chained attached types)
1652 if (type.isType()) {
1653 if (auto result = memberEnumType(type.scope(), name); result.isValid())
1654 return result;
1655 }
1656
1657 if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
1658 if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
1659 if (!genericType(attached)) {
1660 m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg(
1661 attached->internalName()),
1662 qmlCompiler, attached->sourceLocation());
1663 return {};
1664 } else if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1665 m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg(
1666 contained->internalName()),
1667 qmlCompiler, contained->sourceLocation());
1668 return {};
1669 } else {
1670 const QQmlJSRegisterContent namedType = m_pool->createType(
1671 attachedBase, QQmlJSRegisterContent::InvalidLookupIndex,
1672 QQmlJSRegisterContent::TypeByName, type);
1673
1674 return m_pool->createType(
1675 attached, resultLookupIndex, QQmlJSRegisterContent::Attachment,
1676 namedType);
1677 }
1678 }
1679 }
1680
1681 return {};
1682}
1683
1684QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(
1685 QQmlJSRegisterContent type, const QString &name) const
1686{
1687 QQmlJSRegisterContent result;
1688
1689 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1690 type.containedType(),
1691 [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1692 return checkEnums(mode == QQmlJSScope::NotExtension
1693 ? baseType(scope, type)
1694 : extensionType(scope, type),
1695 name, &result);
1696 })) {
1697 return result;
1698 }
1699
1700 return {};
1701}
1702
1703QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1704 QQmlJSRegisterContent type, const QString &name, int lookupIndex) const
1705{
1706 if (type.isType())
1707 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1708 if (type.isProperty() || type.isMethodCall())
1709 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1710 if (type.isEnumeration()) {
1711 const auto enumeration = type.enumeration();
1712 if (!type.enumMember().isEmpty() || !enumeration.hasKey(name))
1713 return {};
1714 return m_pool->createEnumeration(
1715 enumeration, name, QQmlJSRegisterContent::Enum, type.scope());
1716 }
1717 if (type.isMethod()) {
1718 QQmlJSMetaProperty prop;
1719 prop.setTypeName(u"QJSValue"_s);
1720 prop.setPropertyName(name);
1721 prop.setType(jsValueType());
1722 prop.setIsWritable(true);
1723 return m_pool->createProperty(
1724 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1725 QQmlJSRegisterContent::Property, type);
1726 }
1727 if (type.isImportNamespace()) {
1728 Q_ASSERT(type.scopeType()->isReferenceType());
1729 return registerContentForName(name, type);
1730 }
1731 if (type.isConversion()) {
1732 if (const auto result = memberType(
1733 type, name, type.resultLookupIndex(), lookupIndex);
1734 result.isValid()) {
1735 return result;
1736 }
1737
1738 if (const auto result = memberEnumType(type.scope(), name); result.isValid())
1739 return result;
1740
1741 // If the conversion consists of only undefined and one actual type,
1742 // we can produce the members of that one type.
1743 // If the value is then actually undefined, the result is an exception.
1744
1745 const auto nonVoid = extractNonVoidFromOptionalType(type);
1746
1747 // If the conversion cannot hold the original type, it loses information.
1748 return (!nonVoid.isNull() && canHold(type.conversionResultType(), nonVoid.containedType()))
1749 ? memberType(nonVoid, name, type.resultLookupIndex(), lookupIndex)
1750 : QQmlJSRegisterContent();
1751 }
1752
1753 Q_UNREACHABLE_RETURN({});
1754}
1755
1756QQmlJSRegisterContent QQmlJSTypeResolver::elementType(QQmlJSRegisterContent list) const
1757{
1758 QQmlJSScope::ConstPtr value;
1759
1760 auto valueType = [&](const QQmlJSScope::ConstPtr &scope) -> QQmlJSScope::ConstPtr {
1761 if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1762 return scope->elementType();
1763
1764 if (scope == m_forInIteratorPtr)
1765 return m_sizeType;
1766
1767 if (scope == m_forOfIteratorPtr)
1768 return list.scopeType()->elementType();
1769
1770 if (scope == m_jsValueType || scope == m_varType)
1771 return m_jsValueType;
1772
1773 if (scope == m_stringType)
1774 return m_stringType;
1775
1776 return QQmlJSScope::ConstPtr();
1777 };
1778
1779 value = valueType(list.containedType());
1780
1781 if (value.isNull())
1782 return {};
1783
1784 QQmlJSMetaProperty property;
1785 property.setPropertyName(u"[]"_s);
1786 property.setTypeName(value->internalName());
1787 property.setType(value);
1788
1789 return m_pool->createProperty(
1790 property, QQmlJSRegisterContent::InvalidLookupIndex,
1791 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
1792 list);
1793}
1794
1795QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
1796 const QQmlJSMetaMethod &method, const QQmlJSScope::ConstPtr &returnType,
1797 QQmlJSRegisterContent scope) const
1798{
1799 return m_pool->createMethodCall(method, returnType, scope);
1800}
1801
1802QQmlJSRegisterContent QQmlJSTypeResolver::extensionType(
1803 const QQmlJSScope::ConstPtr &extension, QQmlJSRegisterContent base) const
1804{
1805 return m_pool->createType(
1806 extension, base.resultLookupIndex(), QQmlJSRegisterContent::Extension, base);
1807}
1808
1809/*!
1810 * \internal
1811 * Encodes \a base as a base type of \a derived and returns a QQmlJSRegisterContent.
1812 * "Base type" here is understood the same way as std::is_base_of would understand it.
1813 * That means, if you pass the contained type of \a derived as \a base, then \a derived
1814 * itself is returned.
1815 */
1816QQmlJSRegisterContent QQmlJSTypeResolver::baseType(
1817 const QQmlJSScope::ConstPtr &base, QQmlJSRegisterContent derived) const
1818{
1819 return m_pool->createType(
1820 base, derived.resultLookupIndex(), QQmlJSRegisterContent::BaseType, derived);
1821}
1822
1823/*!
1824 * \internal
1825 * Encodes \a parent as a parent scope of \a child and returns a QQmlJSRegisterContent.
1826 * "Parent scope" here means any scope above, but also _including_ \a child.
1827 * That means, if you pass the contained type of \a child as \a parent, then \a child
1828 * itself is returned.
1829 */
1830QQmlJSRegisterContent QQmlJSTypeResolver::parentScope(
1831 const QQmlJSScope::ConstPtr &parent, QQmlJSRegisterContent child) const
1832{
1833 return m_pool->createType(
1834 parent, child.resultLookupIndex(), QQmlJSRegisterContent::ParentScope, child);
1835}
1836
1837QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
1838 QQmlJSRegisterContent listType, QQmlJS::AST::ForEachType type,
1839 int lookupIndex) const
1840{
1841 const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
1842 ? m_int32Type
1843 : elementType(listType).containedType();
1844
1845 QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
1846 ? m_forInIteratorPtr
1847 : m_forOfIteratorPtr;
1848
1849 QQmlJSMetaProperty prop;
1850 prop.setPropertyName(u"<>"_s);
1851 prop.setTypeName(iteratorPointer->internalName());
1852 prop.setType(iteratorPointer);
1853 return m_pool->createProperty(
1854 prop, lookupIndex,
1855 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
1856 listType);
1857}
1858
1859QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr &type) const
1860{
1861 if (type.isNull())
1862 return {};
1863 if (type == voidType())
1864 return type;
1865 if (type->isScript())
1866 return jsValueType();
1867 if (type->isComposite()) {
1868 if (const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(type))
1869 return nonComposite;
1870
1871 // If we can't find the non-composite base, we really don't know what it is.
1872 return genericType(type);
1873 }
1874 if (type->filePath().isEmpty())
1875 return genericType(type);
1876 return type;
1877}
1878
1880 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to,
1881 QQmlJSRegisterContent scope, QQmlJSRegisterContentPool *pool)
1882{
1883 if (from.isConversion()) {
1884 return pool->createConversion(
1885 from.conversionOrigins(), to,
1886 scope.isValid() ? scope : from.conversionResultScope(),
1887 from.variant(), from.scope());
1888 }
1889
1890 return pool->createConversion(
1891 QList<QQmlJSRegisterContent>{from},
1892 to, scope, from.variant(),
1893 from.scope());
1894}
1895
1896QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1897 QQmlJSRegisterContent from, QQmlJSRegisterContent to) const
1898{
1899 return doConvert(from, to.containedType(), to.scope(), m_pool.get());
1900}
1901
1902QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1903 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to) const
1904{
1905 return doConvert(from, to, QQmlJSRegisterContent(), m_pool.get());
1906}
1907
1908QT_END_NAMESPACE
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
static void assertExtension(const QQmlJSScope::ConstPtr &type, QLatin1String extension)
static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode, bool isMethod)
static QQmlJSRegisterContent doConvert(QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to, QQmlJSRegisterContent scope, QQmlJSRegisterContentPool *pool)
static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent::ContentVariant a, QQmlJSRegisterContent::ContentVariant b)