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
534bool QQmlJSTypeResolver::adjustTrackedType(
535 QQmlJSRegisterContent tracked, const QQmlJSScope::ConstPtr &conversion) const
536{
537 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
538 return true;
539
540 QQmlJSScope::ConstPtr contained = tracked.containedType();
541 QQmlJSScope::ConstPtr result = conversion;
542
543 // Do not adjust to the JavaScript extension of the original type. Rather keep the original
544 // type in that case.
545 QQmlJSUtils::searchBaseAndExtensionTypes(
546 contained, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
547 if (kind != QQmlJSScope::ExtensionJavaScript || scope != result)
548 return false;
549 result = contained;
550 return true;
551 });
552
553
554 // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
555 // we better not change the type.
556 if (!canPrimitivelyConvertFromTo(contained, result)
557 && !canPopulate(result, contained, nullptr)
558 && !selectConstructor(result, contained, nullptr).isValid()) {
559 return false;
560 }
561
562 m_pool->adjustType(tracked, result);
563 return true;
564}
565
566bool QQmlJSTypeResolver::adjustTrackedType(
567 QQmlJSRegisterContent tracked, QQmlJSRegisterContent conversion) const
568{
569 return adjustTrackedType(tracked, conversion.containedType());
570}
571
572bool QQmlJSTypeResolver::adjustTrackedType(
573 QQmlJSRegisterContent tracked, const QList<QQmlJSRegisterContent> &conversions) const
574{
575 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
576 return true;
577
578 QQmlJSScope::ConstPtr result;
579 for (QQmlJSRegisterContent type : conversions)
580 result = merge(type.containedType(), result);
581
582 return adjustTrackedType(tracked, result);
583}
584
585void QQmlJSTypeResolver::adjustOriginalType(
586 QQmlJSRegisterContent tracked, const QQmlJSScope::ConstPtr &conversion) const
587{
588 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
589 return;
590
591 m_pool->generalizeType(tracked, conversion);
592}
593
594void QQmlJSTypeResolver::generalizeType(QQmlJSRegisterContent type) const
595{
596 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
597 return;
598
599 for (QQmlJSRegisterContent orig = type; !orig.isNull(); orig = orig.original()) {
600 if (!orig.shadowed().isValid())
601 m_pool->generalizeType(orig, genericType(orig.containedType()));
602 }
603}
604
605bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
606 const QQmlJSScope::ConstPtr &to) const
607{
608 if (canPrimitivelyConvertFromTo(from, to)
609 || canPopulate(to, from, nullptr)
610 || selectConstructor(to, from, nullptr).isValid()) {
611 return true;
612 }
613
614 // ### need a generic solution for custom cpp types:
615 // if (from->m_hasBoolOverload && equals(to, boolType))
616 // return true;
617
618 // All of these types have QString conversions that require a certain format
619 // TODO: Actually verify these strings or deprecate them.
620 // Some of those type are builtins or should be builtins. We should add code for them
621 // in QQmlJSCodeGenerator::conversion().
622 if (from == m_stringType && !to.isNull()) {
623 const QString toTypeName = to->internalName();
624 if (toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
625 || toTypeName == u"QSize"_s || toTypeName == u"QSizeF"_s
626 || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s) {
627 return true;
628 }
629 }
630
631 return false;
632}
633
634bool QQmlJSTypeResolver::canConvertFromTo(QQmlJSRegisterContent from,
635 QQmlJSRegisterContent to) const
636{
637 return canConvertFromTo(from.containedType(), to.containedType());
638}
639
640static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent::ContentVariant a,
641 QQmlJSRegisterContent::ContentVariant b)
642{
643 return (a == b) ? a : QQmlJSRegisterContent::Unknown;
644}
645
646QQmlJSRegisterContent QQmlJSTypeResolver::merge(
647 QQmlJSRegisterContent a, QQmlJSRegisterContent b) const
648{
649 Q_ASSERT(a != b);
650
651 // We cannot easily provide an operator< for QQmlJSRegisterContent.
652 // Therefore we use qHash and operator== here to deduplicate. That's somewhat inefficient.
653 QSet<QQmlJSRegisterContent> origins;
654
655 QQmlJSRegisterContent aResultScope;
656 if (a.isConversion()) {
657 const auto aOrigins = a.conversionOrigins();
658 for (const auto &aOrigin : aOrigins)
659 origins.insert(aOrigin);
660 aResultScope = a.conversionResultScope();
661 } else {
662 origins.insert(a);
663 aResultScope = a.scope();
664 }
665
666 QQmlJSRegisterContent bResultScope;
667 if (b.isConversion()) {
668 const auto bOrigins = b.conversionOrigins();
669 for (const auto &bOrigin : bOrigins)
670 origins.insert(bOrigin);
671 bResultScope = b.conversionResultScope();
672 } else {
673 origins.insert(b);
674 bResultScope = b.scope();
675 }
676
677 const auto mergeScopes = [&](QQmlJSRegisterContent a, QQmlJSRegisterContent b) {
678 // If they are the same, we don't want to re-track them.
679 // We want the repetition on type mismatches to converge.
680 return (a == b) ? a : merge(a, b);
681 };
682
683 return m_pool->createConversion(
684 origins.values(),
685 merge(a.containedType(), b.containedType()),
686 mergeScopes(aResultScope, bResultScope),
687 mergeVariants(a.variant(), b.variant()),
688 mergeScopes(a.scope(), b.scope()));
689}
690
691QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
692 const QQmlJSScope::ConstPtr &b) const
693{
694 if (a.isNull())
695 return b;
696
697 if (b.isNull())
698 return a;
699
700 const auto baseOrExtension
701 = [](const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
702 QQmlJSScope::ConstPtr found;
703 QQmlJSUtils::searchBaseAndExtensionTypes(
704 a, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
705 switch (kind) {
706 case QQmlJSScope::NotExtension:
707 // If b inherits scope, then scope is a common base type of a and b
708 if (b->inherits(scope)) {
709 found = scope;
710 return true;
711 }
712 break;
713 case QQmlJSScope::ExtensionJavaScript:
714 // Merging a type with its JavaScript extension produces the type.
715 // Giving the JavaScript extension as type to be read means we expect any type
716 // that fulfills the given JavaScript interface
717 if (scope == b) {
718 found = a;
719 return true;
720 }
721 break;
722 case QQmlJSScope::ExtensionType:
723 case QQmlJSScope::ExtensionNamespace:
724 break;
725 }
726 return false;
727 });
728 return found;
729 };
730
731 if (a == b)
732 return a;
733
734 if (a == jsValueType() || a == varType())
735 return a;
736 if (b == jsValueType() || b == varType())
737 return b;
738
739 const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
740 return (isIntegral(type)
741 && type != uint32Type()
742 && type != int64Type()
743 && type != uint64Type())
744 || type == boolType();
745 };
746
747 if (isInt32Compatible(a) && isInt32Compatible(b))
748 return int32Type();
749
750 const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
751 return (isUnsignedInteger(type) && type != uint64Type()) || type == boolType();
752 };
753
754 if (isUInt32Compatible(a) && isUInt32Compatible(b))
755 return uint32Type();
756
757 if (isNumeric(a) && isNumeric(b))
758 return realType();
759
760 if (isPrimitive(a) && isPrimitive(b))
761 return jsPrimitiveType();
762
763 if (const auto base = baseOrExtension(a, b))
764 return base;
765
766 if (const auto base = baseOrExtension(b, a))
767 return base;
768
769 if ((a == nullType() || a == boolType()) && b->isReferenceType())
770 return b;
771
772 if ((b == nullType() || b == boolType()) && a->isReferenceType())
773 return a;
774
775 return varType();
776}
777
778bool QQmlJSTypeResolver::canHold(
779 const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
780{
781 if (container == contained || container == m_varType || container == m_jsValueType)
782 return true;
783
784 if (container == m_jsPrimitiveType)
785 return isPrimitive(contained);
786
787 if (container == m_variantListType)
788 return contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
789
790 if (container == m_qObjectListType || container == m_listPropertyType) {
791 if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
792 return false;
793 if (QQmlJSScope::ConstPtr element = contained->elementType())
794 return element->isReferenceType();
795 return false;
796 }
797
798 if (QQmlJSUtils::searchBaseAndExtensionTypes(
799 container, [&](const QQmlJSScope::ConstPtr &base) {
800 return base == contained;
801 })) {
802 return true;
803 }
804
805 if (container->isReferenceType()) {
806 if (QQmlJSUtils::searchBaseAndExtensionTypes(
807 contained, [&](const QQmlJSScope::ConstPtr &base) {
808 return base == container;
809 })) {
810 return true;
811 }
812 }
813
814 return false;
815}
816
817
818bool QQmlJSTypeResolver::canHoldUndefined(QQmlJSRegisterContent content) const
819{
820 const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
821 return type == m_voidType || type == m_varType
822 || type == m_jsValueType || type == m_jsPrimitiveType;
823 };
824
825 if (!canBeUndefined(content.containedType()))
826 return false;
827
828 if (!content.isConversion())
829 return true;
830
831 const auto origins = content.conversionOrigins();
832 for (const auto &origin : origins) {
833 if (canBeUndefined(originalContainedType(origin)))
834 return true;
835 }
836
837 return false;
838}
839
840bool QQmlJSTypeResolver::isOptionalType(QQmlJSRegisterContent content) const
841{
842 if (!content.isConversion())
843 return false;
844
845 const auto origins = content.conversionOrigins();
846 if (origins.length() != 2)
847 return false;
848
849 // Conversion origins are always adjusted to the conversion result. None of them will be void.
850 // Therefore, retrieve the originals first.
851
852 return original(origins[0]).contains(m_voidType) || original(origins[1]).contains(m_voidType);
853}
854
855QQmlJSRegisterContent QQmlJSTypeResolver::extractNonVoidFromOptionalType(
856 QQmlJSRegisterContent content) const
857{
858 if (!isOptionalType(content))
859 return QQmlJSRegisterContent();
860
861 // Conversion origins are always adjusted to the conversion result. None of them will be void.
862 // Therefore, retrieve the originals first.
863
864 auto origins = content.conversionOrigins();
865 std::transform(origins.cbegin(), origins.cend(), origins.begin(),
866 [this](QQmlJSRegisterContent content) {return original(content);});
867 const QQmlJSRegisterContent result = origins[0].contains(m_voidType)
868 ? origins[1]
869 : origins[0];
870
871 // The result may still be undefined. You can write "undefined ?? undefined ?? 1"
872 return result;
873}
874
875QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
876 const QQmlJSScope::ConstPtr &type,
877 ComponentIsGeneric allowComponent) const
878{
879 if (type->isScript())
880 return m_jsValueType;
881
882 if (type == m_metaObjectType)
883 return m_metaObjectType;
884
885 if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
886 QString unresolvedBaseTypeName;
887 for (auto base = type; base;) {
888 // QObject and QQmlComponent are the two required base types.
889 // Any QML type system has to define those, or use the ones from builtins.
890 // As QQmlComponent is derived from QObject, we can restrict ourselves to the latter.
891 // This results in less if'ery when retrieving a QObject* from somewhere and deciding
892 // what it is.
893 if (base->internalName() == u"QObject"_s) {
894 return base;
895 } else if (allowComponent == ComponentIsGeneric::Yes
896 && base->internalName() == u"QQmlComponent"_s) {
897 return base;
898 }
899
900 if (auto baseBase = base->baseType()) {
901 base = baseBase;
902 } else {
903 unresolvedBaseTypeName = base->baseTypeName();
904 break;
905 }
906 }
907
908 // Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
909 // We don't want to deal with those, but m_jsValueType is the best generic option.
910 if (type->filePath().isEmpty())
911 return m_jsValueType;
912
913 m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent. "
914 "You may need to fully qualify all names in C++ so that moc can see them. "
915 "You may also need to add qt_extract_metatypes(<target containing %2>)."_s
916 .arg(type->internalName(), unresolvedBaseTypeName),
917 qmlCompiler, type->sourceLocation());
918
919 // If it does have a filePath, it's some C++ type which we haven't fully resolved.
920 return m_jsValueType;
921 }
922
923 if (type->isListProperty())
924 return m_listPropertyType;
925
926 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
927 return type->baseType();
928
929 if (isPrimitive(type)) {
930 // If the filePath is set, the type is storable and we can just return it.
931 if (!type->filePath().isEmpty())
932 return type;
933
934 // If the type is JavaScript's 'number' type, it's not directly storable, but still
935 // primitive. We use C++ 'double' then.
936 if (isNumeric(type))
937 return m_realType;
938
939 // Otherwise we use QJSPrimitiveValue.
940 // TODO: JavaScript's 'string' and 'boolean' could be special-cased here.
941 return m_jsPrimitiveType;
942 }
943
944 for (const QQmlJSScope::ConstPtr &builtin : {
945 m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
946 m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
947 m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
948 m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
949 m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
950 m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
951 if (type == builtin || type == builtin->listType())
952 return type;
953 }
954
955 return m_varType;
956}
957
958/*!
959 * \internal
960 * The type of a JavaScript literal value appearing in script code
961 */
962QQmlJSRegisterContent QQmlJSTypeResolver::literalType(const QQmlJSScope::ConstPtr &type) const
963{
964 return m_pool->createType(
965 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Literal);
966}
967
968/*!
969 * \internal
970 * The type of the result of a JavaScript operation
971 */
972QQmlJSRegisterContent QQmlJSTypeResolver::operationType(const QQmlJSScope::ConstPtr &type) const
973{
974 return m_pool->createType(
975 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Operation);
976}
977
978/*!
979 * \internal
980 * A type named explicitly, for example in "as"-casts or as function annotation.
981 */
982QQmlJSRegisterContent QQmlJSTypeResolver::namedType(const QQmlJSScope::ConstPtr &type) const
983{
984 return m_pool->createType(
985 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::TypeByName);
986}
987
988QQmlJSRegisterContent QQmlJSTypeResolver::syntheticType(const QQmlJSScope::ConstPtr &type) const
989{
990 return m_pool->createType(
991 type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Unknown);
992}
993
994static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
995 bool isMethod)
996{
997 switch (mode) {
998 case QQmlJSScope::NotExtension:
999 case QQmlJSScope::ExtensionType:
1000 case QQmlJSScope::ExtensionJavaScript:
1001 return isMethod
1002 ? QQmlJSRegisterContent::Method
1003 : QQmlJSRegisterContent::Property;
1004 case QQmlJSScope::ExtensionNamespace:
1005 break;
1006 }
1007 Q_UNREACHABLE_RETURN(QQmlJSRegisterContent::Unknown);
1008}
1009
1010static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
1011{
1012 Q_ASSERT(scope->isComposite());
1013 const QTypeRevision revision = QTypeRevision::fromEncodedVersion(memberRevision);
1014
1015 // If the memberRevision is either invalid or 0.0, then everything is allowed.
1016 if (!revision.isValid() || revision == QTypeRevision::zero())
1017 return true;
1018
1019 const QTypeRevision typeRevision = QQmlJSScope::nonCompositeBaseRevision(
1020 {scope->baseType(), scope->baseTypeRevision()});
1021
1022 // If the revision is not valid, we haven't found a non-composite base type.
1023 // There is nothing we can say about the property then.
1024 return typeRevision.isValid() && typeRevision >= revision;
1025}
1026
1027QQmlJSScope::ConstPtr QQmlJSTypeResolver::resolveParentProperty(
1028 const QString &name, const QQmlJSScope::ConstPtr &base,
1029 const QQmlJSScope::ConstPtr &propType) const
1030{
1031 if (m_parentMode != UseDocumentParent || name != base->parentPropertyName())
1032 return propType;
1033
1034 const QQmlJSScope::ConstPtr baseParent = base->parentScope();
1035 if (!baseParent || !baseParent->inherits(propType))
1036 return propType;
1037
1038 const QString defaultPropertyName = baseParent->defaultPropertyName();
1039 if (defaultPropertyName.isEmpty()) // no reason to search for bindings
1040 return propType;
1041
1042 const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
1043 = baseParent->propertyBindings(defaultPropertyName);
1044 for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
1045 if (binding.bindingType() == QQmlSA::BindingType::Object && binding.objectType() == base)
1046 return baseParent;
1047 }
1048
1049 return propType;
1050}
1051
1052/*!
1053 * \internal
1054 *
1055 * Retrieves the type of whatever \a name signifies in the given \a scope.
1056 * \a name can be an ID, a property of the scope, a singleton, an attachment,
1057 * a plain type reference or a JavaScript global.
1058 *
1059 * TODO: The lookup is actually wrong. We cannot really retrieve JavaScript
1060 * globals here because any runtime-inserted context property would
1061 * override them. We still do because we don't have a better solution for
1062 * identifying e.g. the console object, yet.
1063 *
1064 * \a options tells us whether to consider components as bound. If components
1065 * are bound we can retrieve objects identified by ID in outer contexts.
1066 *
1067 * TODO: This is also wrong because we should alternate scopes and contexts when
1068 * traveling the scope/context hierarchy. Currently we have IDs from any
1069 * context override all scope properties if components are considered
1070 * bound. This is mostly because we don't care about outer scopes at all;
1071 * so we cannot determine with certainty whether an ID from a far outer
1072 * context is overridden by a property of a near outer scope. To
1073 * complicate this further, user context properties can also be inserted
1074 * in outer contexts at run time, shadowing names in further removed outer
1075 * scopes and contexts. What we need to do is determine where exactly what
1076 * kind of property can show up and defend against that with additional
1077 * pragmas.
1078 *
1079 * Note: It probably takes at least 3 nested bound components in one document to
1080 * trigger the misbehavior.
1081 */
1082QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopedType(
1083 const QQmlJSScope::ConstPtr &scope, const QString &name,
1084 QQmlJSScopesByIdOptions options) const
1085{
1086 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1087 if (m_objectsById.possibleScopes(name, scope, options, identified)
1088 != QQmlJSScopesById::Success::Yes) {
1089 // Could not determine component boundaries
1090 return {};
1091 }
1092
1093 if (identified.result) {
1094 // Found a definite match
1095 return identified.result;
1096 }
1097
1098 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(scope)) {
1099 QQmlJSScope::ConstPtr result;
1100 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1101 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1102 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1103 return false;
1104
1105 if (found->hasOwnProperty(name)) {
1106 const QQmlJSMetaProperty prop = found->ownProperty(name);
1107 if (!isRevisionAllowed(prop.revision(), scope))
1108 return false;
1109
1110 result = resolveParentProperty(name, base, prop.type());
1111 return true;
1112 }
1113
1114 if (found->hasOwnMethod(name)) {
1115 const auto methods = found->ownMethods(name);
1116 for (const auto &method : methods) {
1117 if (isRevisionAllowed(method.revision(), scope)) {
1118 result = jsValueType();
1119 return true;
1120 }
1121 }
1122 }
1123
1124 return false;
1125 })) {
1126 return result;
1127 }
1128 }
1129
1130 if (QQmlJSScope::ConstPtr result = containedTypeForName(name))
1131 return result;
1132
1133 if (m_jsGlobalObject->hasProperty(name))
1134 return m_jsGlobalObject->property(name).type();
1135
1136 if (m_jsGlobalObject->hasMethod(name))
1137 return jsValueType();
1138
1139 return {};
1140}
1141
1142/*!
1143 * \internal
1144 *
1145 * Same as the other scopedType method, but accepts a QQmlJSRegisterContent and
1146 * also returns one. This way you not only get the type, but also the content
1147 * variant and various meta info.
1148 */
1149QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(QQmlJSRegisterContent scope,
1150 const QString &name, int lookupIndex,
1151 QQmlJSScopesByIdOptions options) const
1152{
1153 const QQmlJSScope::ConstPtr contained = scope.containedType();
1154
1155 QQmlJSScopesById::CertainCallback<QQmlJSScope::ConstPtr> identified;
1156 if (m_objectsById.possibleScopes(name, contained, options, identified)
1157 != QQmlJSScopesById::Success::Yes) {
1158 // Could not determine component boundaries
1159 return {};
1160 }
1161
1162 if (identified.result) {
1163 // Found a definite match
1164 return m_pool->createType(
1165 identified.result, lookupIndex, QQmlJSRegisterContent::ObjectById, scope);
1166 }
1167
1168 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(contained)) {
1169 QQmlJSRegisterContent result;
1170 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1171 base, [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1172 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1173 return false;
1174
1175 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1176 ? scope
1177 : extensionType(found, scope);
1178
1179 if (found->hasOwnProperty(name)) {
1180 QQmlJSMetaProperty prop = found->ownProperty(name);
1181 if (!isRevisionAllowed(prop.revision(), contained))
1182 return false;
1183
1184 prop.setType(resolveParentProperty(name, base, prop.type()));
1185 result = m_pool->createProperty(
1186 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1187 scopeContentVariant(mode, false), resultScope);
1188 return true;
1189 }
1190
1191 if (found->hasOwnMethod(name)) {
1192 auto methods = found->ownMethods(name);
1193 for (auto it = methods.begin(); it != methods.end();) {
1194 if (!isRevisionAllowed(it->revision(), contained))
1195 it = methods.erase(it);
1196 else
1197 ++it;
1198 }
1199 if (methods.isEmpty())
1200 return false;
1201 result = m_pool->createMethod(
1202 methods, jsValueType(), scopeContentVariant(mode, true), resultScope);
1203 return true;
1204 }
1205
1206 // Unqualified enums are not allowed
1207 return false;
1208 })) {
1209 return result;
1210 }
1211 }
1212
1213 QQmlJSRegisterContent result = registerContentForName(name, scope);
1214
1215 if (result.isValid())
1216 return result;
1217
1218 if (m_jsGlobalObject->hasProperty(name)) {
1219 return m_pool->createProperty(
1220 m_jsGlobalObject->property(name), QQmlJSRegisterContent::InvalidLookupIndex,
1221 lookupIndex, QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1222 } else if (m_jsGlobalObject->hasMethod(name)) {
1223 return m_pool->createMethod(
1224 m_jsGlobalObject->methods(name), jsValueType(),
1225 QQmlJSRegisterContent::Property, m_jsGlobalObjectContent);
1226 }
1227
1228 return {};
1229}
1230
1231/*!
1232 * \fn QQmlJSScope::ConstPtr typeForId(const QQmlJSScope::ConstPtr &scope, const QString &name, QQmlJSScopesByIdOptions options) const
1233 *
1234 * \internal
1235 *
1236 * Same as scopedType(), but assumes that the \a name is an ID and only searches
1237 * the context.
1238 *
1239 * TODO: This is just as wrong as scopedType() in that it disregards both scope
1240 * properties overriding context properties and run time context
1241 * properties.
1242 */
1243
1244bool QQmlJSTypeResolver::checkEnums(
1245 QQmlJSRegisterContent scope, const QString &name,
1246 QQmlJSRegisterContent *result) const
1247{
1248 // You can't have lower case enum names in QML, even if we know the enums here.
1249 if (name.isEmpty() || !name.at(0).isUpper())
1250 return false;
1251
1252 const auto enums = scope.containedType()->ownEnumerations();
1253 for (const auto &enumeration : enums) {
1254 if (enumeration.name() == name) {
1255 *result = m_pool->createEnumeration(
1256 enumeration, QString(),
1257 QQmlJSRegisterContent::Enum,
1258 scope);
1259 return true;
1260 }
1261
1262 if (!(scope.containedType()->enforcesScopedEnums() && enumeration.isScoped())
1263 && enumeration.hasKey(name)) {
1264 *result = m_pool->createEnumeration(
1265 enumeration, name,
1266 QQmlJSRegisterContent::Enum,
1267 scope);
1268 return true;
1269 }
1270 }
1271
1272 return false;
1273}
1274
1275bool QQmlJSTypeResolver::canPopulate(
1276 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1277 bool *isExtension) const
1278{
1279 // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
1280 // code in the code generator.
1281
1282 if (type.isNull()
1283 || canHold(passedArgumentType, type)
1284 || isPrimitive(passedArgumentType)
1285 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1286 || !type->isStructured()) {
1287 return false;
1288 }
1289
1290 if (isExtension)
1291 *isExtension = !type->extensionType().scope.isNull();
1292
1293 return true;
1294}
1295
1296QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
1297 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1298 bool *isExtension) const
1299{
1300 // If the "from" type can hold the target type, we should not try to coerce
1301 // it to any constructor argument.
1302 if (type.isNull()
1303 || canHold(passedArgumentType, type)
1304 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1305 || !type->isCreatable()) {
1306 return QQmlJSMetaMethod();
1307 }
1308
1309 auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &ctorProvider) {
1310 QQmlJSMetaMethod candidate;
1311
1312 const auto ownMethods = ctorProvider->ownMethods();
1313 for (const QQmlJSMetaMethod &method : ownMethods) {
1314 if (!method.isConstructor())
1315 continue;
1316
1317 const auto index = method.constructorIndex();
1318 Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
1319
1320 const auto methodArguments = method.parameters();
1321 if (methodArguments.size() != 1)
1322 continue;
1323
1324 const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
1325
1326 // Do not select the copy constructor (also not if disguised via the extension).
1327 if (methodArgumentType == type)
1328 continue;
1329
1330 if (passedArgumentType == methodArgumentType)
1331 return method;
1332
1333 // Do not select further ctors here. We don't want to do multi-step construction as that
1334 // is confusing and easily leads to infinite recursion.
1335 if (!candidate.isValid()
1336 && canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
1337 candidate = method;
1338 }
1339 }
1340
1341 return candidate;
1342 };
1343
1344 if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
1345 const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
1346 if (ctor.isValid()) {
1347 if (isExtension)
1348 *isExtension = true;
1349 return ctor;
1350 }
1351 }
1352
1353 if (isExtension)
1354 *isExtension = false;
1355
1356 return doSelectConstructor(type);
1357}
1358
1359bool QQmlJSTypeResolver::areEquivalentLists(
1360 const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
1361{
1362 const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
1363 { m_stringListType, m_stringType->listType() },
1364 { m_variantListType, m_varType->listType() }
1365 };
1366
1367 for (const auto eq : equivalentLists) {
1368 if ((a == eq[0] && b == eq[1]) || (a == eq[1] && b == eq[0]))
1369 return true;
1370 }
1371
1372 return false;
1373}
1374
1375bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
1376{
1377 // pointers are trivially copyable
1378 if (type->isReferenceType())
1379 return true;
1380
1381 // Enum values are trivially copyable
1382 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
1383 return true;
1384
1385 for (const QQmlJSScope::ConstPtr &trivial : {
1386 m_nullType, m_voidType,
1387 m_boolType, m_metaObjectType,
1388 m_realType, m_floatType,
1389 m_int8Type, m_uint8Type,
1390 m_int16Type, m_uint16Type,
1391 m_int32Type, m_uint32Type,
1392 m_int64Type, m_uint64Type }) {
1393 if (type == trivial)
1394 return true;
1395 }
1396
1397 return false;
1398}
1399
1400bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
1401{
1402 const bool matchByName = !base->isComposite();
1403 for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
1404 derivedBase = derivedBase->baseType()) {
1405 if (derivedBase == base)
1406 return true;
1407 if (matchByName
1408 && !derivedBase->isComposite()
1409 && derivedBase->internalName() == base->internalName()) {
1410 return true;
1411 }
1412 }
1413 return false;
1414}
1415
1416bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
1417 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
1418{
1419 if (from == to)
1420 return true;
1421 if (from == m_varType || to == m_varType)
1422 return true;
1423 if (from == m_jsValueType || to == m_jsValueType)
1424 return true;
1425 if (to == m_qQmlScriptStringType)
1426 return true;
1427 if (isNumeric(from) && isNumeric(to))
1428 return true;
1429 // We can convert everything to bool.
1430 if (to == m_boolType)
1431 return true;
1432
1433 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1434 && to == m_stringType) {
1435 return true;
1436 }
1437
1438 // Yes, our String has number constructors.
1439 if (isNumeric(from) && to == m_stringType)
1440 return true;
1441
1442 // We can convert strings to numbers, but not to enums
1443 if (from == m_stringType && isNumeric(to))
1444 return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
1445
1446 // We can always convert between strings and urls.
1447 if ((from == m_stringType && to == m_urlType)
1448 || (from == m_urlType && to == m_stringType)) {
1449 return true;
1450 }
1451
1452 // We can always convert between strings and byte arrays.
1453 if ((from == m_stringType && to == m_byteArrayType)
1454 || (from == m_byteArrayType && to == m_stringType)) {
1455 return true;
1456 }
1457
1458 if (to == m_voidType)
1459 return true;
1460
1461 if (to.isNull())
1462 return from == m_voidType;
1463
1464 const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
1465 for (const auto &originType : types) {
1466 if (from != originType)
1467 continue;
1468
1469 for (const auto &targetType : types) {
1470 if (to == targetType)
1471 return true;
1472 }
1473
1474 if (to == m_realType)
1475 return true;
1476
1477 break;
1478 }
1479
1480 if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
1481 return true;
1482
1483 if (from == m_jsPrimitiveType) {
1484 // You can cast any primitive to a nullptr
1485 return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
1486 }
1487
1488 if (to == m_jsPrimitiveType)
1489 return isPrimitive(from);
1490
1491 const bool matchByName = !to->isComposite();
1492 Q_ASSERT(!matchByName || !to->internalName().isEmpty());
1493 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1494 from, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind kind) {
1495 switch (kind) {
1496 case QQmlJSScope::NotExtension:
1497 case QQmlJSScope::ExtensionJavaScript:
1498 // Converting to a base type is trivially supported.
1499 // Converting to the JavaScript extension of a type just produces the type itself.
1500 // Giving the JavaScript extension as type to be converted to means we expect any
1501 // result that fulfills the given JavaScript interface.
1502 return scope == to
1503 || (matchByName && scope->internalName() == to->internalName());
1504 case QQmlJSScope::ExtensionType:
1505 case QQmlJSScope::ExtensionNamespace:
1506 break;
1507 }
1508 return false;
1509 })) {
1510 return true;
1511 }
1512
1513 if (from == m_variantListType)
1514 return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
1515
1516 // We can convert anything that fits natively into QJSPrimitiveValue
1517 if (canPrimitivelyConvertFromTo(from, m_jsPrimitiveType)
1518 && canPrimitivelyConvertFromTo(m_jsPrimitiveType, to)) {
1519 return true;
1520 }
1521
1522 if (areEquivalentLists(from, to))
1523 return true;
1524
1525 if (from->isListProperty()
1526 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1527 && canConvertFromTo(from->elementType(), to->elementType())) {
1528 return true;
1529 }
1530
1531 // it is possible to assing a singlar object to a list property if it could be stored in the list
1532 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1533 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1534 && from->inherits(to->elementType())) {
1535 return true;
1536 }
1537
1538 if (to == m_stringType && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1539 return canConvertFromTo(from->elementType(), m_stringType);
1540
1541 return false;
1542}
1543
1544QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
1545 bool isWritable, QQmlJSRegisterContent scope) const
1546{
1547 QQmlJSMetaProperty prop;
1548 prop.setPropertyName(u"length"_s);
1549 prop.setTypeName(u"qsizetype"_s);
1550 prop.setType(sizeType());
1551 prop.setIsWritable(isWritable);
1552 return m_pool->createProperty(
1553 prop, QQmlJSRegisterContent::InvalidLookupIndex,
1554 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Property, scope);
1555}
1556
1557QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1558 QQmlJSRegisterContent type, const QString &name, int baseLookupIndex,
1559 int resultLookupIndex) const
1560{
1561 QQmlJSRegisterContent result;
1562 const QQmlJSScope::ConstPtr contained = type.containedType();
1563
1564 // If we got a plain type reference we have to check the enums of the _scope_.
1565 if (contained == metaObjectType())
1566 return memberEnumType(type.scope(), name);
1567
1568 if (contained == variantMapType() || contained->inherits(qmlPropertyMapType())) {
1569 QQmlJSMetaProperty prop;
1570 prop.setPropertyName(name);
1571 prop.setTypeName(u"QVariant"_s);
1572 prop.setType(varType());
1573 prop.setIsWritable(true);
1574 return m_pool->createProperty(
1575 prop, baseLookupIndex, resultLookupIndex,
1576 QQmlJSRegisterContent::Property, type);
1577 }
1578
1579 if (contained == jsValueType()) {
1580 QQmlJSMetaProperty prop;
1581 prop.setPropertyName(name);
1582 prop.setTypeName(u"QJSValue"_s);
1583 prop.setType(jsValueType());
1584 prop.setIsWritable(true);
1585 return m_pool->createProperty(
1586 prop, baseLookupIndex, resultLookupIndex,
1587 QQmlJSRegisterContent::Property, type);
1588 }
1589
1590 if ((contained == stringType()
1591 || contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1592 && name == u"length"_s) {
1593 return lengthProperty(contained != stringType(), type);
1594 }
1595
1596 const auto check = [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1597 const QQmlJSRegisterContent resultScope = mode == QQmlJSScope::NotExtension
1598 ? baseType(scope, type)
1599 : extensionType(scope, type);
1600
1601 if (mode != QQmlJSScope::ExtensionNamespace) {
1602 if (scope->hasOwnProperty(name)) {
1603 const auto prop = scope->ownProperty(name);
1604 result = m_pool->createProperty(
1605 prop, baseLookupIndex, resultLookupIndex,
1606 QQmlJSRegisterContent::Property, resultScope);
1607 return true;
1608 }
1609
1610 if (scope->hasOwnMethod(name)) {
1611 const auto methods = scope->ownMethods(name);
1612 result = m_pool->createMethod(
1613 methods, jsValueType(), QQmlJSRegisterContent::Method, resultScope);
1614 return true;
1615 }
1616 }
1617 // don't look up enums on ids...
1618 if (type.variant() == QQmlJSRegisterContent::ObjectById)
1619 return false;
1620 return checkEnums(resultScope, name, &result);
1621 };
1622
1623 if (QQmlJSUtils::searchBaseAndExtensionTypes(type.containedType(), check))
1624 return result;
1625
1626 for (auto scope = contained;
1627 scope && (QQmlSA::isFunctionScope(scope->scopeType())
1628 || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
1629 scope = scope->parentScope()) {
1630 if (auto ownIdentifier = scope->ownJSIdentifier(name)) {
1631 QQmlJSMetaProperty prop;
1632 prop.setPropertyName(name);
1633 prop.setTypeName(u"QJSValue"_s);
1634 prop.setType(jsValueType());
1635 prop.setIsWritable(!(ownIdentifier.value().isConst));
1636
1637 return m_pool->createProperty(
1638 prop, baseLookupIndex, resultLookupIndex,
1639 QQmlJSRegisterContent::Property,
1640 parentScope(scope, type));
1641 }
1642 }
1643
1644 // check enums before checking attached types of attached types (chained attached types)
1645 if (type.isType()) {
1646 if (auto result = memberEnumType(type.scope(), name); result.isValid())
1647 return result;
1648 }
1649
1650 if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
1651 if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
1652 if (!genericType(attached)) {
1653 m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg(
1654 attached->internalName()),
1655 qmlCompiler, attached->sourceLocation());
1656 return {};
1657 } else if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1658 m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg(
1659 contained->internalName()),
1660 qmlCompiler, contained->sourceLocation());
1661 return {};
1662 } else {
1663 const QQmlJSRegisterContent namedType = m_pool->createType(
1664 attachedBase, QQmlJSRegisterContent::InvalidLookupIndex,
1665 QQmlJSRegisterContent::TypeByName, type);
1666
1667 return m_pool->createType(
1668 attached, resultLookupIndex, QQmlJSRegisterContent::Attachment,
1669 namedType);
1670 }
1671 }
1672 }
1673
1674 return {};
1675}
1676
1677QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(
1678 QQmlJSRegisterContent type, const QString &name) const
1679{
1680 QQmlJSRegisterContent result;
1681
1682 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1683 type.containedType(),
1684 [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1685 return checkEnums(mode == QQmlJSScope::NotExtension
1686 ? baseType(scope, type)
1687 : extensionType(scope, type),
1688 name, &result);
1689 })) {
1690 return result;
1691 }
1692
1693 return {};
1694}
1695
1696QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1697 QQmlJSRegisterContent type, const QString &name, int lookupIndex) const
1698{
1699 if (type.isType())
1700 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1701 if (type.isProperty() || type.isMethodCall())
1702 return memberType(type, name, type.resultLookupIndex(), lookupIndex);
1703 if (type.isEnumeration()) {
1704 const auto enumeration = type.enumeration();
1705 if (!type.enumMember().isEmpty() || !enumeration.hasKey(name))
1706 return {};
1707 return m_pool->createEnumeration(
1708 enumeration, name, QQmlJSRegisterContent::Enum, type.scope());
1709 }
1710 if (type.isMethod()) {
1711 QQmlJSMetaProperty prop;
1712 prop.setTypeName(u"QJSValue"_s);
1713 prop.setPropertyName(name);
1714 prop.setType(jsValueType());
1715 prop.setIsWritable(true);
1716 return m_pool->createProperty(
1717 prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
1718 QQmlJSRegisterContent::Property, type);
1719 }
1720 if (type.isImportNamespace()) {
1721 Q_ASSERT(type.scopeType()->isReferenceType());
1722 return registerContentForName(name, type);
1723 }
1724 if (type.isConversion()) {
1725 if (const auto result = memberType(
1726 type, name, type.resultLookupIndex(), lookupIndex);
1727 result.isValid()) {
1728 return result;
1729 }
1730
1731 if (const auto result = memberEnumType(type.scope(), name); result.isValid())
1732 return result;
1733
1734 // If the conversion consists of only undefined and one actual type,
1735 // we can produce the members of that one type.
1736 // If the value is then actually undefined, the result is an exception.
1737
1738 const auto nonVoid = extractNonVoidFromOptionalType(type);
1739
1740 // If the conversion cannot hold the original type, it loses information.
1741 return (!nonVoid.isNull() && canHold(type.conversionResultType(), nonVoid.containedType()))
1742 ? memberType(nonVoid, name, type.resultLookupIndex(), lookupIndex)
1743 : QQmlJSRegisterContent();
1744 }
1745
1746 Q_UNREACHABLE_RETURN({});
1747}
1748
1749QQmlJSRegisterContent QQmlJSTypeResolver::elementType(QQmlJSRegisterContent list) const
1750{
1751 QQmlJSScope::ConstPtr value;
1752
1753 auto valueType = [&](const QQmlJSScope::ConstPtr &scope) -> QQmlJSScope::ConstPtr {
1754 if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1755 return scope->elementType();
1756
1757 if (scope == m_forInIteratorPtr)
1758 return m_sizeType;
1759
1760 if (scope == m_forOfIteratorPtr)
1761 return list.scopeType()->elementType();
1762
1763 if (scope == m_jsValueType || scope == m_varType)
1764 return m_jsValueType;
1765
1766 if (scope == m_stringType)
1767 return m_stringType;
1768
1769 return QQmlJSScope::ConstPtr();
1770 };
1771
1772 value = valueType(list.containedType());
1773
1774 if (value.isNull())
1775 return {};
1776
1777 QQmlJSMetaProperty property;
1778 property.setPropertyName(u"[]"_s);
1779 property.setTypeName(value->internalName());
1780 property.setType(value);
1781
1782 return m_pool->createProperty(
1783 property, QQmlJSRegisterContent::InvalidLookupIndex,
1784 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue,
1785 list);
1786}
1787
1788QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
1789 const QQmlJSMetaMethod &method, const QQmlJSScope::ConstPtr &returnType,
1790 QQmlJSRegisterContent scope) const
1791{
1792 return m_pool->createMethodCall(method, returnType, scope);
1793}
1794
1795QQmlJSRegisterContent QQmlJSTypeResolver::extensionType(
1796 const QQmlJSScope::ConstPtr &extension, QQmlJSRegisterContent base) const
1797{
1798 return m_pool->createType(
1799 extension, base.resultLookupIndex(), QQmlJSRegisterContent::Extension, base);
1800}
1801
1802/*!
1803 * \internal
1804 * Encodes \a base as a base type of \a derived and returns a QQmlJSRegisterContent.
1805 * "Base type" here is understood the same way as std::is_base_of would understand it.
1806 * That means, if you pass the contained type of \a derived as \a base, then \a derived
1807 * itself is returned.
1808 */
1809QQmlJSRegisterContent QQmlJSTypeResolver::baseType(
1810 const QQmlJSScope::ConstPtr &base, QQmlJSRegisterContent derived) const
1811{
1812 return m_pool->createType(
1813 base, derived.resultLookupIndex(), QQmlJSRegisterContent::BaseType, derived);
1814}
1815
1816/*!
1817 * \internal
1818 * Encodes \a parent as a parent scope of \a child and returns a QQmlJSRegisterContent.
1819 * "Parent scope" here means any scope above, but also _including_ \a child.
1820 * That means, if you pass the contained type of \a child as \a parent, then \a child
1821 * itself is returned.
1822 */
1823QQmlJSRegisterContent QQmlJSTypeResolver::parentScope(
1824 const QQmlJSScope::ConstPtr &parent, QQmlJSRegisterContent child) const
1825{
1826 return m_pool->createType(
1827 parent, child.resultLookupIndex(), QQmlJSRegisterContent::ParentScope, child);
1828}
1829
1830QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
1831 QQmlJSRegisterContent listType, QQmlJS::AST::ForEachType type,
1832 int lookupIndex) const
1833{
1834 const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
1835 ? m_int32Type
1836 : elementType(listType).containedType();
1837
1838 QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
1839 ? m_forInIteratorPtr
1840 : m_forOfIteratorPtr;
1841
1842 QQmlJSMetaProperty prop;
1843 prop.setPropertyName(u"<>"_s);
1844 prop.setTypeName(iteratorPointer->internalName());
1845 prop.setType(iteratorPointer);
1846 return m_pool->createProperty(
1847 prop, lookupIndex,
1848 QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
1849 listType);
1850}
1851
1852QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr &type) const
1853{
1854 if (type.isNull())
1855 return {};
1856 if (type == voidType())
1857 return type;
1858 if (type->isScript())
1859 return jsValueType();
1860 if (type->isComposite()) {
1861 if (const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(type))
1862 return nonComposite;
1863
1864 // If we can't find the non-composite base, we really don't know what it is.
1865 return genericType(type);
1866 }
1867 if (type->filePath().isEmpty())
1868 return genericType(type);
1869 return type;
1870}
1871
1873 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to,
1874 QQmlJSRegisterContent scope, QQmlJSRegisterContentPool *pool)
1875{
1876 if (from.isConversion()) {
1877 return pool->createConversion(
1878 from.conversionOrigins(), to,
1879 scope.isValid() ? scope : from.conversionResultScope(),
1880 from.variant(), from.scope());
1881 }
1882
1883 return pool->createConversion(
1884 QList<QQmlJSRegisterContent>{from},
1885 to, scope, from.variant(),
1886 from.scope());
1887}
1888
1889QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1890 QQmlJSRegisterContent from, QQmlJSRegisterContent to) const
1891{
1892 return doConvert(from, to.containedType(), to.scope(), m_pool.get());
1893}
1894
1895QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1896 QQmlJSRegisterContent from, const QQmlJSScope::ConstPtr &to) const
1897{
1898 return doConvert(from, to, QQmlJSRegisterContent(), m_pool.get());
1899}
1900
1901QT_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)