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 auto origins = content.conversionOrigins();
871 std::transform(origins.cbegin(), origins.cend(), origins.begin(),
872 [this](QQmlJSRegisterContent content) {return original(content);});
873 const QQmlJSRegisterContent result = origins[0].contains(m_voidType)
874 ? origins[1]
875 : origins[0];
876
877 // The result may still be undefined. You can write "undefined ?? undefined ?? 1"
878 return result;
879}
880
881QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
882 const QQmlJSScope::ConstPtr &type,
883 ComponentIsGeneric allowComponent) const
884{
885 if (type->isScript())
886 return m_jsValueType;
887
888 if (type == m_metaObjectType)
889 return m_metaObjectType;
890
891 if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
892 QString unresolvedBaseTypeName;
893 for (auto base = type; base;) {
894 // QObject and QQmlComponent are the two required base types.
895 // Any QML type system has to define those, or use the ones from builtins.
896 // As QQmlComponent is derived from QObject, we can restrict ourselves to the latter.
897 // This results in less if'ery when retrieving a QObject* from somewhere and deciding
898 // what it is.
899 if (base->internalName() == u"QObject"_s) {
900 return base;
901 } else if (allowComponent == ComponentIsGeneric::Yes
902 && base->internalName() == u"QQmlComponent"_s) {
903 return base;
904 }
905
906 if (auto baseBase = base->baseType()) {
907 base = baseBase;
908 } else {
909 unresolvedBaseTypeName = base->baseTypeName();
910 break;
911 }
912 }
913
914 // Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
915 // We don't want to deal with those, but m_jsValueType is the best generic option.
916 if (type->filePath().isEmpty())
917 return m_jsValueType;
918
919 m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent. "
920 "You may need to fully qualify all names in C++ so that moc can see them. "
921 "You may also need to add qt_extract_metatypes(<target containing %2>)."_s
922 .arg(type->internalName(), unresolvedBaseTypeName),
923 qmlCompiler, type->sourceLocation());
924
925 // If it does have a filePath, it's some C++ type which we haven't fully resolved.
926 return m_jsValueType;
927 }
928
929 if (type->isListProperty())
930 return m_listPropertyType;
931
932 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
933 return type->baseType();
934
935 if (isPrimitive(type)) {
936 // If the filePath is set, the type is storable and we can just return it.
937 if (!type->filePath().isEmpty())
938 return type;
939
940 // If the type is JavaScript's 'number' type, it's not directly storable, but still
941 // primitive. We use C++ 'double' then.
942 if (isNumeric(type))
943 return m_realType;
944
945 // Otherwise we use QJSPrimitiveValue.
946 // TODO: JavaScript's 'string' and 'boolean' could be special-cased here.
947 return m_jsPrimitiveType;
948 }
949
950 for (const QQmlJSScope::ConstPtr &builtin : {
951 m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
952 m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
953 m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
954 m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
955 m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
956 m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
957 if (type == builtin || type == builtin->listType())
958 return type;
959 }
960
961 return m_varType;
962}
963
964/*!
965 * \internal
966 * The type of a JavaScript literal value appearing in script code
967 */
968QQmlJSRegisterContent QQmlJSTypeResolver::literalType(const QQmlJSScope::ConstPtr &type) const
969{
970 return m_pool->createType(
971 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Literal);
972}
973
974/*!
975 * \internal
976 * The type of the result of a JavaScript operation
977 */
978QQmlJSRegisterContent QQmlJSTypeResolver::operationType(const QQmlJSScope::ConstPtr &type) const
979{
980 return m_pool->createType(
981 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Operation);
982}
983
984/*!
985 * \internal
986 * A type named explicitly, for example in "as"-casts or as function annotation.
987 */
988QQmlJSRegisterContent QQmlJSTypeResolver::namedType(const QQmlJSScope::ConstPtr &type) const
989{
990 return m_pool->createType(
991 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::TypeByName);
992}
993
994QQmlJSRegisterContent QQmlJSTypeResolver::syntheticType(const QQmlJSScope::ConstPtr &type) const
995{
996 return m_pool->createType(
997 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Unknown);
998}
999
1000static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
1001 bool isMethod)
1002{
1003 switch (mode) {
1004 case QQmlJSScope::NotExtension:
1005 case QQmlJSScope::ExtensionType:
1006 case QQmlJSScope::ExtensionJavaScript:
1007 return isMethod
1008 ? QQmlJSRegisterContent::Method
1009 : QQmlJSRegisterContent::Property;
1010 case QQmlJSScope::ExtensionNamespace:
1011 break;
1012 }
1013 Q_UNREACHABLE_RETURN(QQmlJSRegisterContent::Unknown);
1014}
1015
1016static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
1017{
1018 Q_ASSERT(scope->isComposite());
1019 const QTypeRevision revision = QTypeRevision::fromEncodedVersion(memberRevision);
1020
1021 // If the memberRevision is either invalid or 0.0, then everything is allowed.
1022 if (!revision.isValid() || revision == QTypeRevision::zero())
1023 return true;
1024
1025 const QTypeRevision typeRevision = QQmlJSScope::nonCompositeBaseRevision(
1026 {scope->baseType(), scope->baseTypeRevision()});
1027
1028 // If the revision is not valid, we haven't found a non-composite base type.
1029 // There is nothing we can say about the property then.
1030 return typeRevision.isValid() && typeRevision >= revision;
1031}
1032
1033QQmlJSScope::ConstPtr QQmlJSTypeResolver::resolveParentProperty(
1034 const QString &name, const QQmlJSScope::ConstPtr &base,
1035 const QQmlJSScope::ConstPtr &propType) const
1036{
1037 if (m_parentMode != UseDocumentParent || name != base->parentPropertyName())
1038 return propType;
1039
1040 const QQmlJSScope::ConstPtr baseParent = base->parentScope();
1041 if (!baseParent || !baseParent->inherits(propType))
1042 return propType;
1043
1044 const QString defaultPropertyName = baseParent->defaultPropertyName();
1045 if (defaultPropertyName.isEmpty()) // no reason to search for bindings
1046 return propType;
1047
1048 const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
1049 = baseParent->propertyBindings(defaultPropertyName);
1050 for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
1051 if (binding.bindingType() == QQmlSA::BindingType::Object && binding.objectType() == base)
1052 return baseParent;
1053 }
1054
1055 return propType;
1056}
1057
1058/*!
1059 * \internal
1060 *
1061 * Retrieves the type of whatever \a name signifies in the given \a scope.
1062 * \a name can be an ID, a property of the scope, a singleton, an attachment,
1063 * a plain type reference or a JavaScript global.
1064 *
1065 * TODO: The lookup is actually wrong. We cannot really retrieve JavaScript
1066 * globals here because any runtime-inserted context property would
1067 * override them. We still do because we don't have a better solution for
1068 * identifying e.g. the console object, yet.
1069 *
1070 * \a options tells us whether to consider components as bound. If components
1071 * are bound we can retrieve objects identified by ID in outer contexts.
1072 *
1073 * TODO: This is also wrong because we should alternate scopes and contexts when
1074 * traveling the scope/context hierarchy. Currently we have IDs from any
1075 * context override all scope properties if components are considered
1076 * bound. This is mostly because we don't care about outer scopes at all;
1077 * so we cannot determine with certainty whether an ID from a far outer
1078 * context is overridden by a property of a near outer scope. To
1079 * complicate this further, user context properties can also be inserted
1080 * in outer contexts at run time, shadowing names in further removed outer
1081 * scopes and contexts. What we need to do is determine where exactly what
1082 * kind of property can show up and defend against that with additional
1083 * pragmas.
1084 *
1085 * Note: It probably takes at least 3 nested bound components in one document to
1086 * trigger the misbehavior.
1087 */
1088QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopedType(
1089 const QQmlJSScope::ConstPtr &scope, const QString &name,
1090 QQmlJSScopesByIdOptions options) const
1091{
1092 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1093 if (m_objectsById.possibleScopes(name, scope, options, identified)
1094 != QQmlJSScopesById::Success::Yes) {
1095 // Could not determine component boundaries
1096 return {};
1097 }
1098
1099 if (identified.result) {
1100 // Found a definite match
1101 return identified.result;
1102 }
1103
1104 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(scope)) {
1105 QQmlJSScope::ConstPtr result;
1106 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1107 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1108 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1109 return false;
1110
1111 if (found->hasOwnProperty(name)) {
1112 const QQmlJSMetaProperty prop = found->ownProperty(name);
1113 if (!isRevisionAllowed(prop.revision(), scope))
1114 return false;
1115
1116 result = resolveParentProperty(name, base, prop.type());
1117 return true;
1118 }
1119
1120 if (found->hasOwnMethod(name)) {
1121 const auto methods = found->ownMethods(name);
1122 for (const auto &method : methods) {
1123 if (isRevisionAllowed(method.revision(), scope)) {
1124 result = jsValueType();
1125 return true;
1126 }
1127 }
1128 }
1129
1130 return false;
1131 })) {
1132 return result;
1133 }
1134 }
1135
1136 if (QQmlJSScope::ConstPtr result = containedTypeForName(name))
1137 return result;
1138
1139 if (m_jsGlobalObject->hasProperty(name))
1140 return m_jsGlobalObject->property(name).type();
1141
1142 if (m_jsGlobalObject->hasMethod(name))
1143 return jsValueType();
1144
1145 return {};
1146}
1147
1148/*!
1149 * \internal
1150 *
1151 * Same as the other scopedType method, but accepts a QQmlJSRegisterContent and
1152 * also returns one. This way you not only get the type, but also the content
1153 * variant and various meta info.
1154 */
1155QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(QQmlJSRegisterContent scope,
1156 const QString &name, int lookupIndex,
1157 QQmlJSScopesByIdOptions options) const
1158{
1159 const QQmlJSScope::ConstPtr contained = scope.containedType();
1160
1161 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1162 if (m_objectsById.possibleScopes(name, contained, options, identified)
1163 != QQmlJSScopesById::Success::Yes) {
1164 // Could not determine component boundaries
1165 return {};
1166 }
1167
1168 if (identified.result) {
1169 // Found a definite match
1170 return m_pool->createType(
1171 identified.result, lookupIndex, QQmlJSRegisterContent::ObjectById, scope);
1172 }
1173
1174 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(contained)) {
1175 QQmlJSRegisterContent result;
1176 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1177 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1178 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1179 return false;
1180
1181 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1182 ? scope
1183 : extensionType(found, scope);
1184
1185 if (found->hasOwnProperty(name)) {
1186 QQmlJSMetaProperty prop = found->ownProperty(name);
1187 if (!isRevisionAllowed(prop.revision(), contained))
1188 return false;
1189
1190 prop.setType(resolveParentProperty(name, base, prop.type()));
1191 result = m_pool->createProperty(
1192 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1193 scopeContentVariant(mode, false), resultScope);
1194 return true;
1195 }
1196
1197 if (found->hasOwnMethod(name)) {
1198 auto methods = found->ownMethods(name);
1199 for (auto it = methods.begin(); it != methods.end();) {
1200 if (!isRevisionAllowed(it->revision(), contained))
1201 it = methods.erase(it);
1202 else
1203 ++it;
1204 }
1205 if (methods.isEmpty())
1206 return false;
1207 result = m_pool->createMethod(
1208 methods, jsValueType(), scopeContentVariant(mode, true), resultScope);
1209 return true;
1210 }
1211
1212 // Unqualified enums are not allowed
1213 return false;
1214 })) {
1215 return result;
1216 }
1217 }
1218
1219 QQmlJSRegisterContent result = registerContentForName(name, scope);
1220
1221 if (result.isValid())
1222 return result;
1223
1224 if (m_jsGlobalObject->hasProperty(name)) {
1225 return m_pool->createProperty(
1226 m_jsGlobalObject->property(name), QQmlJSRegisterContent::InvalidLookupIndex,
1227 lookupIndex, QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1228 } else if (m_jsGlobalObject->hasMethod(name)) {
1229 return m_pool->createMethod(
1230 m_jsGlobalObject->methods(name), jsValueType(),
1231 QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1232 }
1233
1234 return {};
1235}
1236
1237/*!
1238 * \fn QQmlJSScope::ConstPtr typeForId(const QQmlJSScope::ConstPtr &scope, const QString &name, QQmlJSScopesByIdOptions options) const
1239 *
1240 * \internal
1241 *
1242 * Same as scopedType(), but assumes that the \a name is an ID and only searches
1243 * the context.
1244 *
1245 * TODO: This is just as wrong as scopedType() in that it disregards both scope
1246 * properties overriding context properties and run time context
1247 * properties.
1248 */
1249
1250bool QQmlJSTypeResolver::checkEnums(
1251 QQmlJSRegisterContent scope, const QString &name,
1252 QQmlJSRegisterContent *result) const
1253{
1254 // You can't have lower case enum names in QML, even if we know the enums here.
1255 if (name.isEmpty() || !name.at(0).isUpper())
1256 return false;
1257
1258 const auto enums = scope.containedType()->ownEnumerations();
1259 for (const auto &enumeration : enums) {
1260 if (enumeration.name() == name) {
1261 *result = m_pool->createEnumeration(
1262 enumeration, QString(),
1263 QQmlJSRegisterContent::Enum,
1264 scope);
1265 return true;
1266 }
1267
1268 if (!(scope.containedType()->enforcesScopedEnums() && enumeration.isScoped())
1269 && enumeration.hasKey(name)) {
1270 *result = m_pool->createEnumeration(
1271 enumeration, name,
1272 QQmlJSRegisterContent::Enum,
1273 scope);
1274 return true;
1275 }
1276 }
1277
1278 return false;
1279}
1280
1281bool QQmlJSTypeResolver::canPopulate(
1282 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1283 bool *isExtension) const
1284{
1285 // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
1286 // code in the code generator.
1287
1288 if (type.isNull()
1289 || canHold(passedArgumentType, type)
1290 || isPrimitive(passedArgumentType)
1291 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1292 || !type->isStructured()) {
1293 return false;
1294 }
1295
1296 if (isExtension)
1297 *isExtension = !type->extensionType().scope.isNull();
1298
1299 return true;
1300}
1301
1302QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
1303 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1304 bool *isExtension) const
1305{
1306 // If the "from" type can hold the target type, we should not try to coerce
1307 // it to any constructor argument.
1308 if (type.isNull()
1309 || canHold(passedArgumentType, type)
1310 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1311 || !type->isCreatable()) {
1312 return QQmlJSMetaMethod();
1313 }
1314
1315 auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &ctorProvider) {
1316 QQmlJSMetaMethod candidate;
1317
1318 const auto ownMethods = ctorProvider->ownMethods();
1319 for (const QQmlJSMetaMethod &method : ownMethods) {
1320 if (!method.isConstructor())
1321 continue;
1322
1323 const auto index = method.constructorIndex();
1324 Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
1325
1326 const auto methodArguments = method.parameters();
1327 if (methodArguments.size() != 1)
1328 continue;
1329
1330 const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
1331
1332 // Do not select the copy constructor (also not if disguised via the extension).
1333 if (methodArgumentType == type)
1334 continue;
1335
1336 if (passedArgumentType == methodArgumentType)
1337 return method;
1338
1339 // Do not select further ctors here. We don't want to do multi-step construction as that
1340 // is confusing and easily leads to infinite recursion.
1341 if (!candidate.isValid()
1342 && canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
1343 candidate = method;
1344 }
1345 }
1346
1347 return candidate;
1348 };
1349
1350 if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
1351 const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
1352 if (ctor.isValid()) {
1353 if (isExtension)
1354 *isExtension = true;
1355 return ctor;
1356 }
1357 }
1358
1359 if (isExtension)
1360 *isExtension = false;
1361
1362 return doSelectConstructor(type);
1363}
1364
1365bool QQmlJSTypeResolver::areEquivalentLists(
1366 const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
1367{
1368 const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
1369 { m_stringListType, m_stringType->listType() },
1370 { m_variantListType, m_varType->listType() }
1371 };
1372
1373 for (const auto eq : equivalentLists) {
1374 if ((a == eq[0] && b == eq[1]) || (a == eq[1] && b == eq[0]))
1375 return true;
1376 }
1377
1378 return false;
1379}
1380
1381bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
1382{
1383 // pointers are trivially copyable
1384 if (type->isReferenceType())
1385 return true;
1386
1387 // Enum values are trivially copyable
1388 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
1389 return true;
1390
1391 for (const QQmlJSScope::ConstPtr &trivial : {
1392 m_nullType, m_voidType,
1393 m_boolType, m_metaObjectType,
1394 m_realType, m_floatType,
1395 m_int8Type, m_uint8Type,
1396 m_int16Type, m_uint16Type,
1397 m_int32Type, m_uint32Type,
1398 m_int64Type, m_uint64Type }) {
1399 if (type == trivial)
1400 return true;
1401 }
1402
1403 return false;
1404}
1405
1406bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
1407{
1408 const bool matchByName = !base->isComposite();
1409 for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
1410 derivedBase = derivedBase->baseType()) {
1411 if (derivedBase == base)
1412 return true;
1413 if (matchByName
1414 && !derivedBase->isComposite()
1415 && derivedBase->internalName() == base->internalName()) {
1416 return true;
1417 }
1418 }
1419 return false;
1420}
1421
1422bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
1423 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
1424{
1425 if (from == to)
1426 return true;
1427 if (from == m_varType || to == m_varType)
1428 return true;
1429 if (from == m_jsValueType || to == m_jsValueType)
1430 return true;
1431 if (to == m_qQmlScriptStringType)
1432 return true;
1433 if (isNumeric(from) && isNumeric(to))
1434 return true;
1435 // We can convert everything to bool.
1436 if (to == m_boolType)
1437 return true;
1438
1439 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1440 && to == m_stringType) {
1441 return true;
1442 }
1443
1444 // Yes, our String has number constructors.
1445 if (isNumeric(from) && to == m_stringType)
1446 return true;
1447
1448 // We can convert strings to numbers, but not to enums
1449 if (from == m_stringType && isNumeric(to))
1450 return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
1451
1452 // We can always convert between strings and urls.
1453 if ((from == m_stringType && to == m_urlType)
1454 || (from == m_urlType && to == m_stringType)) {
1455 return true;
1456 }
1457
1458 // We can always convert between strings and byte arrays.
1459 if ((from == m_stringType && to == m_byteArrayType)
1460 || (from == m_byteArrayType && to == m_stringType)) {
1461 return true;
1462 }
1463
1464 if (to == m_voidType)
1465 return true;
1466
1467 if (to.isNull())
1468 return from == m_voidType;
1469
1470 const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
1471 for (const auto &originType : types) {
1472 if (from != originType)
1473 continue;
1474
1475 for (const auto &targetType : types) {
1476 if (to == targetType)
1477 return true;
1478 }
1479
1480 if (to == m_realType)
1481 return true;
1482
1483 break;
1484 }
1485
1486 if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
1487 return true;
1488
1489 if (from == m_jsPrimitiveType) {
1490 // You can cast any primitive to a nullptr
1491 return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
1492 }
1493
1494 if (to == m_jsPrimitiveType)
1495 return isPrimitive(from);
1496
1497 const bool matchByName = !to->isComposite();
1498 Q_ASSERT(!matchByName || !to->internalName().isEmpty());
1499 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1500 from, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
1501 switch (kind) {
1502 case QQmlJSScope::NotExtension:
1503 case QQmlJSScope::ExtensionJavaScript:
1504 // Converting to a base type is trivially supported.
1505 // Converting to the JavaScript extension of a type just produces the type itself.
1506 // Giving the JavaScript extension as type to be converted to means we expect any
1507 // result that fulfills the given JavaScript interface.
1508 return scope == to
1509 || (matchByName && scope->internalName() == to->internalName());
1510 case QQmlJSScope::ExtensionType:
1511 case QQmlJSScope::ExtensionNamespace:
1512 break;
1513 }
1514 return false;
1515 })) {
1516 return true;
1517 }
1518
1519 if (from == m_variantListType)
1520 return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
1521
1522 // We can convert anything that fits natively into QJSPrimitiveValue
1523 if (canPrimitivelyConvertFromTo(from, m_jsPrimitiveType)
1524 && canPrimitivelyConvertFromTo(m_jsPrimitiveType, to)) {
1525 return true;
1526 }
1527
1528 if (areEquivalentLists(from, to))
1529 return true;
1530
1531 if (from->isListProperty()
1532 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1533 && canConvertFromTo(from->elementType(), to->elementType())) {
1534 return true;
1535 }
1536
1537 // it is possible to assing a singlar object to a list property if it could be stored in the list
1538 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1539 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1540 && from->inherits(to->elementType())) {
1541 return true;
1542 }
1543
1544 if (to == m_stringType && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1545 return canConvertFromTo(from->elementType(), m_stringType);
1546
1547 return false;
1548}
1549
1550QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
1551 bool isWritable, QQmlJSRegisterContent scope) const
1552{
1553 QQmlJSMetaProperty prop;
1554 prop.setPropertyName(u"length"_s);
1555 prop.setTypeName(u"qsizetype"_s);
1556 prop.setType(sizeType());
1557 prop.setIsWritable(isWritable);
1558 return m_pool->createProperty(
1559 prop, QQmlJSRegisterContent::InvalidLookupIndex,
1560 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Property, scope);
1561}
1562
1563QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1564 QQmlJSRegisterContent type, const QString &name, int baseLookupIndex,
1565 int resultLookupIndex) const
1566{
1567 QQmlJSRegisterContent result;
1568 const QQmlJSScope::ConstPtr contained = type.containedType();
1569
1570 // If we got a plain type reference we have to check the enums of the _scope_.
1571 if (contained == metaObjectType())
1572 return memberEnumType(type.scope(), name);
1573
1574 if (contained == variantMapType() || contained->inherits(qmlPropertyMapType())) {
1575 QQmlJSMetaProperty prop;
1576 prop.setPropertyName(name);
1577 prop.setTypeName(u"QVariant"_s);
1578 prop.setType(varType());
1579 prop.setIsWritable(true);
1580 return m_pool->createProperty(
1581 prop, baseLookupIndex, resultLookupIndex,
1582 QQmlJSRegisterContent::Property, type);
1583 }
1584
1585 if (contained == jsValueType()) {
1586 QQmlJSMetaProperty prop;
1587 prop.setPropertyName(name);
1588 prop.setTypeName(u"QJSValue"_s);
1589 prop.setType(jsValueType());
1590 prop.setIsWritable(true);
1591 return m_pool->createProperty(
1592 prop, baseLookupIndex, resultLookupIndex,
1593 QQmlJSRegisterContent::Property, type);
1594 }
1595
1596 if ((contained == stringType()
1597 || contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1598 && name == u"length"_s) {
1599 return lengthProperty(contained != stringType(), type);
1600 }
1601
1602 const auto check = [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1603 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1604 ? baseType(scope, type)
1605 : extensionType(scope, type);
1606
1607 if (mode != QQmlJSScope::ExtensionNamespace) {
1608 if (scope->hasOwnProperty(name)) {
1609 const auto prop = scope->ownProperty(name);
1610 result = m_pool->createProperty(
1611 prop, baseLookupIndex, resultLookupIndex,
1612 QQmlJSRegisterContent::Property, resultScope);
1613 return true;
1614 }
1615
1616 if (scope->hasOwnMethod(name)) {
1617 const auto methods = scope->ownMethods(name);
1618 result = m_pool->createMethod(
1619 methods, jsValueType(), QQmlJSRegisterContent::Method, resultScope);
1620 return true;
1621 }
1622 }
1623 // don't look up enums on ids...
1624 if (type.variant() == QQmlJSRegisterContent::ObjectById)
1625 return false;
1626 return checkEnums(resultScope, name, &result);
1627 };
1628
1629 if (QQmlJSUtils::searchBaseAndExtensionTypes(type.containedType(), check))
1630 return result;
1631
1632 for (auto scope = contained;
1633 scope && (QQmlSA::isFunctionScope(scope->scopeType())
1634 || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
1635 scope = scope->parentScope()) {
1636 if (auto ownIdentifier = scope->ownJSIdentifier(name)) {
1637 QQmlJSMetaProperty prop;
1638 prop.setPropertyName(name);
1639 prop.setTypeName(u"QJSValue"_s);
1640 prop.setType(jsValueType());
1641 prop.setIsWritable(!(ownIdentifier.value().isConst));
1642
1643 return m_pool->createProperty(
1644 prop, baseLookupIndex, resultLookupIndex,
1645 QQmlJSRegisterContent::Property,
1646 parentScope(scope, type));
1647 }
1648 }
1649
1650 // check enums before checking attached types of attached types (chained attached types)
1651 if (type.isType()) {
1652 if (auto result = memberEnumType(type.scope(), name); result.isValid())
1653 return result;
1654 }
1655
1656 if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
1657 if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
1658 if (!genericType(attached)) {
1659 m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg(
1660 attached->internalName()),
1661 qmlCompiler, attached->sourceLocation());
1662 return {};
1663 } else if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1664 m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg(
1665 contained->internalName()),
1666 qmlCompiler, contained->sourceLocation());
1667 return {};
1668 } else {
1669 const QQmlJSRegisterContent namedType = m_pool->createType(
1670 attachedBase, QQmlJSRegisterContent::InvalidLookupIndex,
1671 QQmlJSRegisterContent::TypeByName, type);
1672
1673 return m_pool->createType(
1674 attached, resultLookupIndex, QQmlJSRegisterContent::Attachment,
1675 namedType);
1676 }
1677 }
1678 }
1679
1680 return {};
1681}
1682
1683QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(
1684 QQmlJSRegisterContent type, const QString &name) const
1685{
1686 QQmlJSRegisterContent result;
1687
1688 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1689 type.containedType(),
1690 [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1691 return checkEnums(mode == QQmlJSScope::NotExtension
1692 ? baseType(scope, type)
1693 : extensionType(scope, type),
1694 name, &result);
1695 })) {
1696 return result;
1697 }
1698
1699 return {};
1700}
1701
1702QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1703 QQmlJSRegisterContent type, const QString &name, int lookupIndex) const
1704{
1705 if (type.isType())
1706 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1707 if (type.isProperty() || type.isMethodCall())
1708 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1709 if (type.isEnumeration()) {
1710 const auto enumeration = type.enumeration();
1711 if (!type.enumMember().isEmpty() || !enumeration.hasKey(name))
1712 return {};
1713 return m_pool->createEnumeration(
1714 enumeration, name, QQmlJSRegisterContent::Enum, type.scope());
1715 }
1716 if (type.isMethod()) {
1717 QQmlJSMetaProperty prop;
1718 prop.setTypeName(u"QJSValue"_s);
1719 prop.setPropertyName(name);
1720 prop.setType(jsValueType());
1721 prop.setIsWritable(true);
1722 return m_pool->createProperty(
1723 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1724 QQmlJSRegisterContent::Property, type);
1725 }
1726 if (type.isImportNamespace()) {
1727 Q_ASSERT(type.scopeType()->isReferenceType());
1728 return registerContentForName(name, type);
1729 }
1730 if (type.isConversion()) {
1731 if (const auto result = memberType(
1732 type, name, type.resultLookupIndex(), lookupIndex);
1733 result.isValid()) {
1734 return result;
1735 }
1736
1737 if (const auto result = memberEnumType(type.scope(), name); result.isValid())
1738 return result;
1739
1740 // If the conversion consists of only undefined and one actual type,
1741 // we can produce the members of that one type.
1742 // If the value is then actually undefined, the result is an exception.
1743
1744 const auto nonVoid = extractNonVoidFromOptionalType(type);
1745
1746 // If the conversion cannot hold the original type, it loses information.
1747 return (!nonVoid.isNull() && canHold(type.conversionResultType(), nonVoid.containedType()))
1748 ? memberType(nonVoid, name, type.resultLookupIndex(), lookupIndex)
1749 : QQmlJSRegisterContent();
1750 }
1751
1752 Q_UNREACHABLE_RETURN({});
1753}
1754
1755QQmlJSRegisterContent QQmlJSTypeResolver::elementType(QQmlJSRegisterContent list) const
1756{
1757 QQmlJSScope::ConstPtr value;
1758
1759 auto valueType = [&](const QQmlJSScope::ConstPtr &scope) -> QQmlJSScope::ConstPtr {
1760 if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1761 return scope->elementType();
1762
1763 if (scope == m_forInIteratorPtr)
1764 return m_sizeType;
1765
1766 if (scope == m_forOfIteratorPtr)
1767 return list.scopeType()->elementType();
1768
1769 if (scope == m_jsValueType || scope == m_varType)
1770 return m_jsValueType;
1771
1772 if (scope == m_stringType)
1773 return m_stringType;
1774
1775 return QQmlJSScope::ConstPtr();
1776 };
1777
1778 value = valueType(list.containedType());
1779
1780 if (value.isNull())
1781 return {};
1782
1783 QQmlJSMetaProperty property;
1784 property.setPropertyName(u"[]"_s);
1785 property.setTypeName(value->internalName());
1786 property.setType(value);
1787
1788 return m_pool->createProperty(
1789 property, QQmlJSRegisterContent::InvalidLookupIndex,
1790 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
1791 list);
1792}
1793
1794QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
1795 const QQmlJSMetaMethod &method, const QQmlJSScope::ConstPtr &returnType,
1796 QQmlJSRegisterContent scope) const
1797{
1798 return m_pool->createMethodCall(method, returnType, scope);
1799}
1800
1801QQmlJSRegisterContent QQmlJSTypeResolver::extensionType(
1802 const QQmlJSScope::ConstPtr &extension, QQmlJSRegisterContent base) const
1803{
1804 return m_pool->createType(
1805 extension, base.resultLookupIndex(), QQmlJSRegisterContent::Extension, base);
1806}
1807
1808/*!
1809 * \internal
1810 * Encodes \a base as a base type of \a derived and returns a QQmlJSRegisterContent.
1811 * "Base type" here is understood the same way as std::is_base_of would understand it.
1812 * That means, if you pass the contained type of \a derived as \a base, then \a derived
1813 * itself is returned.
1814 */
1815QQmlJSRegisterContent QQmlJSTypeResolver::baseType(
1816 const QQmlJSScope::ConstPtr &base, QQmlJSRegisterContent derived) const
1817{
1818 return m_pool->createType(
1819 base, derived.resultLookupIndex(), QQmlJSRegisterContent::BaseType, derived);
1820}
1821
1822/*!
1823 * \internal
1824 * Encodes \a parent as a parent scope of \a child and returns a QQmlJSRegisterContent.
1825 * "Parent scope" here means any scope above, but also _including_ \a child.
1826 * That means, if you pass the contained type of \a child as \a parent, then \a child
1827 * itself is returned.
1828 */
1829QQmlJSRegisterContent QQmlJSTypeResolver::parentScope(
1830 const QQmlJSScope::ConstPtr &parent, QQmlJSRegisterContent child) const
1831{
1832 return m_pool->createType(
1833 parent, child.resultLookupIndex(), QQmlJSRegisterContent::ParentScope, child);
1834}
1835
1836QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
1837 QQmlJSRegisterContent listType, QQmlJS::AST::ForEachType type,
1838 int lookupIndex) const
1839{
1840 const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
1841 ? m_int32Type
1842 : elementType(listType).containedType();
1843
1844 QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
1845 ? m_forInIteratorPtr
1846 : m_forOfIteratorPtr;
1847
1848 QQmlJSMetaProperty prop;
1849 prop.setPropertyName(u"<>"_s);
1850 prop.setTypeName(iteratorPointer->internalName());
1851 prop.setType(iteratorPointer);
1852 return m_pool->createProperty(
1853 prop, lookupIndex,
1854 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
1855 listType);
1856}
1857
1858QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr &type) const
1859{
1860 if (type.isNull())
1861 return {};
1862 if (type == voidType())
1863 return type;
1864 if (type->isScript())
1865 return jsValueType();
1866 if (type->isComposite()) {
1867 if (const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(type))
1868 return nonComposite;
1869
1870 // If we can't find the non-composite base, we really don't know what it is.
1871 return genericType(type);
1872 }
1873 if (type->filePath().isEmpty())
1874 return genericType(type);
1875 return type;
1876}
1877
1879 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to,
1880 QQmlJSRegisterContent scope, QQmlJSRegisterContentPool *pool)
1881{
1882 if (from.isConversion()) {
1883 return pool->createConversion(
1884 from.conversionOrigins(), to,
1885 scope.isValid() ? scope : from.conversionResultScope(),
1886 from.variant(), from.scope());
1887 }
1888
1889 return pool->createConversion(
1890 QList<QQmlJSRegisterContent>{from},
1891 to, scope, from.variant(),
1892 from.scope());
1893}
1894
1895QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1896 QQmlJSRegisterContent from, QQmlJSRegisterContent to) const
1897{
1898 return doConvert(from, to.containedType(), to.scope(), m_pool.get());
1899}
1900
1901QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1902 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to) const
1903{
1904 return doConvert(from, to, QQmlJSRegisterContent(), m_pool.get());
1905}
1906
1907QT_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)