aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljstyperesolver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljstyperesolver.cpp')
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp868
1 files changed, 680 insertions, 188 deletions
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 901eccf387..93f9c7f7f4 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -19,42 +19,124 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcTypeResolver, "qt.qml.compiler.typeresolver", QtInfoMsg);
+static inline void assertExtension(const QQmlJSScope::ConstPtr &type, QLatin1String extension)
+{
+ Q_ASSERT(type);
+ Q_ASSERT(type->extensionType().scope->internalName() == extension);
+ Q_ASSERT(type->extensionIsJavaScript());
+}
+
QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
- : m_imports(importer->builtinInternalNames())
- , m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
+ : m_imports(importer->builtinInternalNames()),
+ m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
{
const QQmlJSImporter::ImportedTypes &builtinTypes = m_imports;
+
m_voidType = builtinTypes.type(u"void"_s).scope;
+ assertExtension(m_voidType, "undefined"_L1);
+
m_nullType = builtinTypes.type(u"std::nullptr_t"_s).scope;
+ Q_ASSERT(m_nullType);
+
m_realType = builtinTypes.type(u"double"_s).scope;
+ assertExtension(m_realType, "Number"_L1);
+
m_floatType = builtinTypes.type(u"float"_s).scope;
- m_intType = builtinTypes.type(u"int"_s).scope;
- m_uintType = builtinTypes.type(u"uint"_s).scope;
+ assertExtension(m_floatType, "Number"_L1);
+
+ m_int8Type = builtinTypes.type(u"qint8"_s).scope;
+ assertExtension(m_int8Type, "Number"_L1);
+
+ m_uint8Type = builtinTypes.type(u"quint8"_s).scope;
+ assertExtension(m_uint8Type, "Number"_L1);
+
+ m_int16Type = builtinTypes.type(u"short"_s).scope;
+ assertExtension(m_int16Type, "Number"_L1);
+
+ m_uint16Type = builtinTypes.type(u"ushort"_s).scope;
+ assertExtension(m_uint16Type, "Number"_L1);
+
+ m_int32Type = builtinTypes.type(u"int"_s).scope;
+ assertExtension(m_int32Type, "Number"_L1);
+
+ m_uint32Type = builtinTypes.type(u"uint"_s).scope;
+ assertExtension(m_uint32Type, "Number"_L1);
+
+ m_int64Type = builtinTypes.type(u"qlonglong"_s).scope;
+ Q_ASSERT(m_int64Type);
+
+ m_uint64Type = builtinTypes.type(u"qulonglong"_s).scope;
+ Q_ASSERT(m_uint64Type);
+
+ m_sizeType = builtinTypes.type(u"qsizetype"_s).scope;
+ assertExtension(m_sizeType, "Number"_L1);
+
+ // qsizetype is either a 32bit or a 64bit signed integer. We don't want to special-case it.
+ Q_ASSERT(m_sizeType == m_int32Type || m_sizeType == m_int64Type);
+
m_boolType = builtinTypes.type(u"bool"_s).scope;
+ assertExtension(m_boolType, "Boolean"_L1);
+
m_stringType = builtinTypes.type(u"QString"_s).scope;
+ assertExtension(m_stringType, "String"_L1);
+
m_stringListType = builtinTypes.type(u"QStringList"_s).scope;
+ assertExtension(m_stringListType, "Array"_L1);
+
m_byteArrayType = builtinTypes.type(u"QByteArray"_s).scope;
+ assertExtension(m_byteArrayType, "ArrayBuffer"_L1);
+
m_urlType = builtinTypes.type(u"QUrl"_s).scope;
+ assertExtension(m_urlType, "URL"_L1);
+
m_dateTimeType = builtinTypes.type(u"QDateTime"_s).scope;
+ assertExtension(m_dateTimeType, "Date"_L1);
+
m_dateType = builtinTypes.type(u"QDate"_s).scope;
+ Q_ASSERT(m_dateType);
+
m_timeType = builtinTypes.type(u"QTime"_s).scope;
+ Q_ASSERT(m_timeType);
+
m_variantListType = builtinTypes.type(u"QVariantList"_s).scope;
+ assertExtension(m_variantListType, "Array"_L1);
+
+ m_variantMapType = builtinTypes.type(u"QVariantMap"_s).scope;
+ Q_ASSERT(m_variantMapType);
m_varType = builtinTypes.type(u"QVariant"_s).scope;
+ Q_ASSERT(m_varType);
+
m_jsValueType = builtinTypes.type(u"QJSValue"_s).scope;
- m_listPropertyType = builtinTypes.type(u"QQmlListProperty<QObject>"_s).scope;
+ Q_ASSERT(m_jsValueType);
+
+ m_qObjectType = builtinTypes.type(u"QObject"_s).scope;
+ assertExtension(m_qObjectType, "Object"_L1);
+
m_qObjectListType = builtinTypes.type(u"QObjectList"_s).scope;
+ assertExtension(m_qObjectListType, "Array"_L1);
+
+ m_qQmlScriptStringType = builtinTypes.type(u"QQmlScriptString"_s).scope;
+ Q_ASSERT(m_qQmlScriptStringType);
+
+ m_functionType = builtinTypes.type(u"function"_s).scope;
+ Q_ASSERT(m_functionType);
+
+ m_numberPrototype = builtinTypes.type(u"NumberPrototype"_s).scope;
+ Q_ASSERT(m_numberPrototype);
+
+ m_arrayPrototype = builtinTypes.type(u"ArrayPrototype"_s).scope;
+ Q_ASSERT(m_arrayPrototype);
+
+ m_listPropertyType = m_qObjectType->listType();
+ Q_ASSERT(m_listPropertyType->internalName() == u"QQmlListProperty<QObject>"_s);
+ Q_ASSERT(m_listPropertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence);
+ Q_ASSERT(m_listPropertyType->valueTypeName() == u"QObject"_s);
+ assertExtension(m_listPropertyType, "Array"_L1);
QQmlJSScope::Ptr emptyType = QQmlJSScope::create();
emptyType->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
m_emptyType = emptyType;
- QQmlJSScope::Ptr emptyListType = QQmlJSScope::create();
- emptyListType->setInternalName(u"void*"_s);
- emptyListType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
- QQmlJSScope::resolveTypes(emptyListType, builtinTypes);
- Q_ASSERT(!emptyListType->extensionType().scope.isNull());
- m_emptyListType = emptyListType;
-
QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_s);
jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_s);
@@ -67,29 +149,26 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
m_metaObjectType = metaObjectType;
- QQmlJSScope::Ptr functionType = QQmlJSScope::create();
- functionType->setInternalName(u"function"_s);
- functionType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
- m_functionType = functionType;
-
m_jsGlobalObject = importer->jsGlobalObject();
- auto numberMethods = m_jsGlobalObject->methods(u"Number"_s);
- Q_ASSERT(numberMethods.size() == 1);
- m_numberPrototype = numberMethods[0].returnType()->baseType();
- Q_ASSERT(m_numberPrototype);
- Q_ASSERT(m_numberPrototype->internalName() == u"NumberPrototype"_s);
- auto arrayMethods = m_jsGlobalObject->methods(u"Array"_s);
- Q_ASSERT(arrayMethods.size() == 1);
- m_arrayType = arrayMethods[0].returnType();
- Q_ASSERT(m_arrayType);
+ QQmlJSScope::Ptr forInIteratorPtr = QQmlJSScope::create();
+ forInIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
+ forInIteratorPtr->setFilePath(u"qjslist.h"_s);
+ forInIteratorPtr->setInternalName(u"QJSListForInIterator::Ptr"_s);
+ m_forInIteratorPtr = forInIteratorPtr;
+
+ QQmlJSScope::Ptr forOfIteratorPtr = QQmlJSScope::create();
+ forOfIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
+ forOfIteratorPtr->setFilePath(u"qjslist.h"_s);
+ forOfIteratorPtr->setInternalName(u"QJSListForOfIterator::Ptr"_s);
+ m_forOfIteratorPtr = forOfIteratorPtr;
}
/*!
\internal
Initializes the type resolver. As part of that initialization, makes \a
- visitor traverse the program.
+ visitor traverse the program when given.
*/
void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program)
{
@@ -100,7 +179,8 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
m_imports.clearTypes();
m_signalHandlers.clear();
- program->accept(visitor);
+ if (program)
+ program->accept(visitor);
m_objectsById = visitor->addressableScopes();
m_objectsByLocation = visitor->scopesBylocation();
@@ -108,6 +188,16 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
m_imports = visitor->imports();
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::mathObject() const
+{
+ return jsGlobalObject()->property(u"Math"_s).type();
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::consoleObject() const
+{
+ return jsGlobalObject()->property(u"console"_s).type();
+}
+
QQmlJSScope::ConstPtr
QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
{
@@ -117,19 +207,15 @@ QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location
return m_objectsByLocation[location];
}
-QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
- const QString &id, const QQmlJSScope::ConstPtr &referrer) const
-{
- return m_objectsById.scope(id, referrer);
-}
-
QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
{
const QString typeId = QmlIR::IRBuilder::asString(type->typeId);
if (!type->typeArgument)
return m_imports.type(typeId).scope;
- if (typeId == u"list"_s)
- return typeForName(type->typeArgument->toString())->listType();
+ if (typeId == u"list"_s) {
+ if (const QQmlJSScope::ConstPtr typeArgument = typeForName(type->typeArgument->toString()))
+ return typeArgument->listType();
+ }
return QQmlJSScope::ConstPtr();
}
@@ -140,7 +226,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) co
return voidType();
if (value.isInt32())
- return intType();
+ return int32Type();
if (value.isBoolean())
return boolType();
@@ -180,9 +266,9 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::BitXor:
case QSOperator::Op::LShift:
case QSOperator::Op::RShift:
- return builtinType(intType());
+ return builtinType(int32Type());
case QSOperator::Op::URShift:
- return builtinType(uintType());
+ return builtinType(uint32Type());
case QSOperator::Op::Add: {
const auto leftContents = containedType(left);
const auto rightContents = containedType(right);
@@ -191,7 +277,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
if (equals(result, boolType()))
- return builtinType(intType());
+ return builtinType(int32Type());
if (isNumeric(result))
return builtinType(realType());
@@ -201,7 +287,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::Mul:
case QSOperator::Op::Exp: {
const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right));
- return builtinType(equals(result, boolType()) ? intType() : realType());
+ return builtinType(equals(result, boolType()) ? int32Type() : realType());
}
case QSOperator::Op::Div:
case QSOperator::Op::Mod:
@@ -222,14 +308,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
case UnaryOperator::Not:
return builtinType(boolType());
case UnaryOperator::Complement:
- return builtinType(intType());
+ return builtinType(int32Type());
case UnaryOperator::Plus:
if (isIntegral(operand))
return operand;
Q_FALLTHROUGH();
default:
if (equals(containedType(operand), boolType()))
- return builtinType(intType());
+ return builtinType(int32Type());
break;
}
@@ -248,13 +334,18 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isIntegral(const QQmlJSRegisterContent &type) const
{
- return equals(containedType(type), m_intType) || equals(containedType(type), m_uintType);
+ return isIntegral(containedType(type));
+}
+
+bool QQmlJSTypeResolver::isIntegral(const QQmlJSScope::ConstPtr &type) const
+{
+ // Only types of length <= 32bit count as integral
+ return isSignedInteger(type) || isUnsignedInteger(type);
}
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
- return equals(type, m_intType) || equals(type, m_uintType)
- || equals(type, m_realType) || equals(type, m_floatType)
+ return isNumeric(type)
|| equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType)
|| equals(type, m_stringType) || equals(type, m_jsPrimitiveType);
}
@@ -266,7 +357,33 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
if (mode == QQmlJSScope::ExtensionNamespace)
return false;
return equals(scope, m_numberPrototype);
- });
+ });
+}
+
+bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(type, m_int8Type)
+ || equals(type, m_int16Type)
+ || equals(type, m_int32Type)
+ || equals(type, m_int64Type);
+}
+
+bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(type, m_uint8Type)
+ || equals(type, m_uint16Type)
+ || equals(type, m_uint32Type)
+ || equals(type, m_uint64Type);
+}
+
+bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
+{
+ return (equals(type, m_uint8Type)
+ || equals(type, m_int8Type)
+ || equals(type, m_uint16Type)
+ || equals(type, m_int16Type)
+ || equals(type, m_uint32Type)
+ || equals(type, m_int32Type));
}
QQmlJSScope::ConstPtr
@@ -317,15 +434,15 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
if (origin.isType()) {
return QQmlJSRegisterContent::create(
(this->*op)(origin.storedType()), (this->*op)(origin.type()),
- origin.variant(), (this->*op)(origin.scopeType()));
+ origin.resultLookupIndex(), origin.variant(), (this->*op)(origin.scopeType()));
}
if (origin.isProperty()) {
QQmlJSMetaProperty prop = origin.property();
prop.setType((this->*op)(prop.type()));
return QQmlJSRegisterContent::create(
- (this->*op)(origin.storedType()), prop,
- origin.variant(), (this->*op)(origin.scopeType()));
+ (this->*op)(origin.storedType()), prop, origin.baseLookupIndex(),
+ origin.resultLookupIndex(), origin.variant(), (this->*op)(origin.scopeType()));
}
if (origin.isEnumeration()) {
@@ -349,16 +466,31 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
}
if (origin.isConversion()) {
+ // When retrieving the originals we want a deep retrieval.
+ // When tracking a new type, we don't want to re-track its originals, though.
+
+ const QList<QQmlJSScope::ConstPtr> origins = origin.conversionOrigins();
+ QList<QQmlJSScope::ConstPtr> transformedOrigins;
+ if (op == &QQmlJSTypeResolver::trackedType) {
+ transformedOrigins = origins;
+ } else {
+ transformedOrigins.reserve(origins.length());
+ for (const QQmlJSScope::ConstPtr &origin: origins)
+ transformedOrigins.append((this->*op)(origin));
+ }
+
return QQmlJSRegisterContent::create(
- (this->*op)(origin.storedType()), origin.conversionOrigins(),
+ (this->*op)(origin.storedType()),
+ transformedOrigins,
(this->*op)(origin.conversionResult()),
+ (this->*op)(origin.conversionResultScope()),
origin.variant(), (this->*op)(origin.scopeType()));
}
Q_UNREACHABLE_RETURN({});
}
-QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
+QQmlJSRegisterContent QQmlJSTypeResolver::registerContentForName(
const QString &name, const QQmlJSScope::ConstPtr &scopeType,
bool hasObjectModulePrefix) const
{
@@ -366,13 +498,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
if (!type)
return QQmlJSRegisterContent();
- if (type->isSingleton())
- return QQmlJSRegisterContent::create(storedType(type), type,
- QQmlJSRegisterContent::Singleton, scopeType);
+ if (type->isSingleton()) {
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Singleton, scopeType);
+ }
- if (type->isScript())
- return QQmlJSRegisterContent::create(storedType(type), type,
- QQmlJSRegisterContent::Script, scopeType);
+ if (type->isScript()) {
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Script, scopeType);
+ }
if (const auto attached = type->attachedType()) {
if (!genericType(attached)) {
@@ -390,7 +526,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
// mode, we will figure this out using the scope type and access any enums of the
// plain type directly. In indirect mode, we can use enum lookups.
return QQmlJSRegisterContent::create(
- storedType(attached), attached,
+ storedType(attached), attached, QQmlJSRegisterContent::InvalidLookupIndex,
hasObjectModulePrefix
? QQmlJSRegisterContent::ObjectAttached
: QQmlJSRegisterContent::ScopeAttached, type);
@@ -404,11 +540,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
// We may still need the plain type reference for enum lookups,
// Store it as QMetaObject.
// This only works with namespaces and object types.
- return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(),
- QQmlJSRegisterContent::MetaType, type);
+ return QQmlJSRegisterContent::create(
+ metaObjectType(), metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::MetaType, type);
case QQmlJSScope::AccessSemantics::Sequence:
case QQmlJSScope::AccessSemantics::Value:
- // This is not actually a type reference. You cannot get the metaobject
+ if (canAddressValueTypes()) {
+ return QQmlJSRegisterContent::create(
+ metaObjectType(), metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::MetaType, type);
+ }
+ // Else this is not actually a type reference. You cannot get the metaobject
// of a value type in QML and sequences don't even have metaobjects.
break;
}
@@ -439,23 +581,33 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
return originalType(containedType(container));
}
-void QQmlJSTypeResolver::adjustTrackedType(
+bool QQmlJSTypeResolver::adjustTrackedType(
const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
{
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
- return;
+ return true;
const auto it = m_trackedTypes->find(tracked);
Q_ASSERT(it != m_trackedTypes->end());
+
+ // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
+ // we better not change the type.
+ if (!canPrimitivelyConvertFromTo(tracked, conversion)
+ && !canPopulate(conversion, tracked, nullptr)
+ && !selectConstructor(conversion, tracked, nullptr).isValid()) {
+ return false;
+ }
+
it->replacement = comparableType(conversion);
*it->clone = std::move(*QQmlJSScope::clone(conversion));
+ return true;
}
-void QQmlJSTypeResolver::adjustTrackedType(
+bool QQmlJSTypeResolver::adjustTrackedType(
const QQmlJSScope::ConstPtr &tracked, const QList<QQmlJSScope::ConstPtr> &conversions) const
{
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
- return;
+ return true;
const auto it = m_trackedTypes->find(tracked);
Q_ASSERT(it != m_trackedTypes->end());
@@ -466,10 +618,28 @@ void QQmlJSTypeResolver::adjustTrackedType(
// If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
// we better not change the type.
- if (canPrimitivelyConvertFromTo(tracked, result)) {
- it->replacement = comparableType(result);
- *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ if (!canPrimitivelyConvertFromTo(tracked, result)
+ && !canPopulate(result, tracked, nullptr)
+ && !selectConstructor(result, tracked, nullptr).isValid()) {
+ return false;
}
+
+ it->replacement = comparableType(result);
+ *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ return true;
+}
+
+void QQmlJSTypeResolver::adjustOriginalType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
+{
+ if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
+ return;
+
+ const auto it = m_trackedTypes->find(tracked);
+ Q_ASSERT(it != m_trackedTypes->end());
+
+ it->original = conversion;
+ *it->clone = std::move(*QQmlJSScope::clone(conversion));
}
void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
@@ -512,8 +682,11 @@ QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &conta
bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
- if (canPrimitivelyConvertFromTo(from, to))
+ if (canPrimitivelyConvertFromTo(from, to)
+ || canPopulate(to, from, nullptr)
+ || selectConstructor(to, from, nullptr).isValid()) {
return true;
+ }
// ### need a generic solution for custom cpp types:
// if (from->m_hasBoolOverload && equals(to, boolType))
@@ -525,11 +698,9 @@ bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
// in QQmlJSCodeGenerator::conversion().
if (equals(from, m_stringType) && !to.isNull()) {
const QString toTypeName = to->internalName();
- if (toTypeName == u"QTime"_s || toTypeName == u"QDate"_s
- || toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
+ if (toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
|| toTypeName == u"QSize"_s || toTypeName == u"QSizeF"_s
- || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s
- || toTypeName == u"QColor"_s) {
+ || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s) {
return true;
}
}
@@ -552,16 +723,28 @@ static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent
QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
const QQmlJSRegisterContent &b) const
{
+ if (a == b)
+ return a;
+
QList<QQmlJSScope::ConstPtr> origins;
- if (a.isConversion())
+
+ QQmlJSScope::ConstPtr aResultScope;
+ if (a.isConversion()) {
origins.append(a.conversionOrigins());
- else
+ aResultScope = a.conversionResultScope();
+ } else {
origins.append(containedType(a));
+ aResultScope = a.scopeType();
+ }
- if (b.isConversion())
+ QQmlJSScope::ConstPtr bResultScope;
+ if (b.isConversion()) {
origins.append(b.conversionOrigins());
- else
+ bResultScope = b.conversionResultScope();
+ } else {
origins.append(containedType(b));
+ bResultScope = b.scopeType();
+ }
std::sort(origins.begin(), origins.end());
const auto erase = std::unique(origins.begin(), origins.end());
@@ -571,6 +754,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
merge(a.storedType(), b.storedType()),
origins,
merge(containedType(a), containedType(b)),
+ merge(aResultScope, bResultScope),
mergeVariants(a.variant(), b.variant()),
merge(a.scopeType(), b.scopeType()));
}
@@ -605,36 +789,81 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (equals(b, jsValueType()) || equals(b, varType()))
return b;
- auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) {
- return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to));
+ const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
+ return (isIntegral(type) && !equals(type, uint32Type())) || equals(type, boolType());
+ };
+
+ if (isInt32Compatible(a) && isInt32Compatible(b))
+ return int32Type();
+
+ const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
+ return isUnsignedInteger(type) || equals(type, boolType());
};
+ if (isUInt32Compatible(a) && isUInt32Compatible(b))
+ return uint32Type();
+
if (isNumeric(a) && isNumeric(b))
return realType();
- if (canConvert(boolType(), intType()))
- return intType();
- if (canConvert(boolType(), uintType()))
- return uintType();
- if (canConvert(intType(), stringType()))
- return stringType();
- if (canConvert(uintType(), stringType()))
- return stringType();
if (isPrimitive(a) && isPrimitive(b))
return jsPrimitiveType();
if (auto commonBase = commonBaseType(a, b))
return commonBase;
- if (equals(a, nullType()) && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if ((equals(a, nullType()) || equals(a, boolType())) && b->isReferenceType())
return b;
- if (equals(b, nullType()) && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if ((equals(b, nullType()) || equals(b, boolType())) && a->isReferenceType())
return a;
return varType();
}
+bool QQmlJSTypeResolver::canHold(
+ const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
+{
+ if (equals(container, contained)
+ || equals(container, m_varType)
+ || equals(container, m_jsValueType)) {
+ return true;
+ }
+
+ if (equals(container, m_jsPrimitiveType))
+ return isPrimitive(contained);
+
+ if (equals(container, m_variantListType))
+ return contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
+
+ if (equals(container, m_qObjectListType) || equals(container, m_listPropertyType)) {
+ if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
+ return false;
+ if (QQmlJSScope::ConstPtr value = contained->valueType())
+ return value->isReferenceType();
+ return false;
+ }
+
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(
+ container, [&](const QQmlJSScope::ConstPtr &base) {
+ return equals(base, contained);
+ })) {
+ return true;
+ }
+
+ if (container->isReferenceType()) {
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(
+ contained, [&](const QQmlJSScope::ConstPtr &base) {
+ return equals(base, container);
+ })) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content) const
{
const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
@@ -657,6 +886,30 @@ bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content)
return false;
}
+bool QQmlJSTypeResolver::isOptionalType(const QQmlJSRegisterContent &content) const
+{
+ if (!content.isConversion())
+ return false;
+
+ const auto origins = content.conversionOrigins();
+ if (origins.length() != 2)
+ return false;
+
+ return equals(origins[0], m_voidType) || equals(origins[1], m_voidType);
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::extractNonVoidFromOptionalType(
+ const QQmlJSRegisterContent &content) const
+{
+ if (!isOptionalType(content))
+ return QQmlJSScope::ConstPtr();
+
+ const auto origins = content.conversionOrigins();
+ const QQmlJSScope::ConstPtr result = equals(origins[0], m_voidType) ? origins[1] : origins[0];
+ Q_ASSERT(!equals(result, m_voidType));
+ return result;
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
const QQmlJSScope::ConstPtr &type,
ComponentIsGeneric allowComponent) const
@@ -704,33 +957,21 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
if (type->isListProperty())
return m_listPropertyType;
- if (isPrimitive(type) || equals(type, m_jsValueType)
- || equals(type, m_urlType) || equals(type, m_dateTimeType)
- || equals(type, m_dateType) || equals(type, m_timeType)
- || equals(type, m_variantListType) || equals(type, m_varType)
- || equals(type, m_stringListType) || equals(type, m_emptyListType)
- || equals(type, m_byteArrayType)) {
- return type;
- }
-
- if (type->scopeType() == QQmlJSScope::EnumScope)
- return m_intType;
+ if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return type->baseType();
- if (isNumeric(type))
- return m_realType;
+ if (isPrimitive(type))
+ return type;
- if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
- if (const QQmlJSScope::ConstPtr valueType = type->valueType()) {
- switch (valueType->accessSemantics()) {
- case QQmlJSScope::AccessSemantics::Value:
- return genericType(valueType)->listType();
- case QQmlJSScope::AccessSemantics::Reference:
- return m_qObjectListType;
- default:
- break;
- }
- }
- return m_variantListType;
+ for (const QQmlJSScope::ConstPtr &builtin : {
+ m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
+ m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
+ m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
+ m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
+ m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
+ m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
+ if (equals(type, builtin) || equals(type, builtin->listType()))
+ return type;
}
return m_varType;
@@ -739,12 +980,15 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
QQmlJSRegisterContent QQmlJSTypeResolver::builtinType(const QQmlJSScope::ConstPtr &type) const
{
Q_ASSERT(storedType(type) == type);
- return QQmlJSRegisterContent::create(type, type, QQmlJSRegisterContent::Builtin);
+ return QQmlJSRegisterContent::create(
+ type, type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin);
}
QQmlJSRegisterContent QQmlJSTypeResolver::globalType(const QQmlJSScope::ConstPtr &type) const
{
- return QQmlJSRegisterContent::create(storedType(type), type, QQmlJSRegisterContent::Unknown);
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Unknown);
}
static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
@@ -754,6 +998,7 @@ static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::Ex
case QQmlJSScope::NotExtension:
return isMethod ? QQmlJSRegisterContent::ScopeMethod : QQmlJSRegisterContent::ScopeProperty;
case QQmlJSScope::ExtensionType:
+ case QQmlJSScope::ExtensionJavaScript:
return isMethod ? QQmlJSRegisterContent::ExtensionScopeMethod
: QQmlJSRegisterContent::ExtensionScopeProperty;
case QQmlJSScope::ExtensionNamespace:
@@ -780,7 +1025,8 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s
}
QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
- const QString &name) const
+ const QString &name, int lookupIndex,
+ QQmlJSScopesByIdOptions options) const
{
const auto isAssignedToDefaultProperty = [this](const QQmlJSScope::ConstPtr &parent,
const QQmlJSScope::ConstPtr &child) {
@@ -791,7 +1037,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
const QList<QQmlJSMetaPropertyBinding> defaultPropBindings =
parent->propertyBindings(defaultPropertyName);
for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
+ if (binding.bindingType() == QQmlSA::BindingType::Object
&& equals(binding.objectType(), child)) {
return true;
}
@@ -799,8 +1045,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
return false;
};
- if (QQmlJSScope::ConstPtr identified = scopeForId(name, scope)) {
- return QQmlJSRegisterContent::create(storedType(identified), identified,
+ if (QQmlJSScope::ConstPtr identified = m_objectsById.scope(name, scope, options)) {
+ return QQmlJSRegisterContent::create(storedType(identified), identified, lookupIndex,
QQmlJSRegisterContent::ObjectById, scope);
}
@@ -823,8 +1069,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
}
}
result = QQmlJSRegisterContent::create(
- storedType(prop.type()),
- prop, scopeContentVariant(mode, false), scope);
+ storedType(prop.type()), prop,
+ QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ scopeContentVariant(mode, false), scope);
return true;
}
@@ -851,14 +1098,16 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
}
}
- QQmlJSRegisterContent result = referenceTypeForName(name);
+ QQmlJSRegisterContent result = registerContentForName(name);
+
if (result.isValid())
return result;
if (m_jsGlobalObject->hasProperty(name)) {
- return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->property(name),
- QQmlJSRegisterContent::JavaScriptGlobal,
- m_jsGlobalObject);
+ return QQmlJSRegisterContent::create(
+ jsValueType(), m_jsGlobalObject->property(name),
+ QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ QQmlJSRegisterContent::JavaScriptGlobal, m_jsGlobalObject);
} else if (m_jsGlobalObject->hasMethod(name)) {
return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->methods(name),
QQmlJSRegisterContent::JavaScriptGlobal,
@@ -876,23 +1125,22 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
if (name.isEmpty() || !name.at(0).isUpper())
return false;
- const bool inExtension =
- (mode == QQmlJSScope::ExtensionType) || (mode == QQmlJSScope::ExtensionNamespace);
+ const bool inExtension = (mode != QQmlJSScope::NotExtension);
const auto enums = scope->ownEnumerations();
for (const auto &enumeration : enums) {
- if (enumeration.name() == name) {
+ if ((enumeration.isScoped() || enumeration.isQml()) && enumeration.name() == name) {
*result = QQmlJSRegisterContent::create(
- storedType(intType()), enumeration, QString(),
+ storedType(enumeration.type()), enumeration, QString(),
inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
: QQmlJSRegisterContent::ObjectEnum,
scope);
return true;
}
- if (enumeration.hasKey(name)) {
+ if (!enumeration.isScoped() && enumeration.hasKey(name)) {
*result = QQmlJSRegisterContent::create(
- storedType(intType()), enumeration, name,
+ storedType(enumeration.type()), enumeration, name,
inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
: QQmlJSRegisterContent::ObjectEnum,
scope);
@@ -903,6 +1151,143 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
return false;
}
+bool QQmlJSTypeResolver::canPopulate(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
+ bool *isExtension) const
+{
+ // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
+ // code in the code generator.
+
+ if (type.isNull()
+ || canHold(passedArgumentType, type)
+ || isPrimitive(passedArgumentType)
+ || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
+ || !type->isStructured()) {
+ return false;
+ }
+
+ if (isExtension)
+ *isExtension = !type->extensionType().scope.isNull();
+
+ return true;
+}
+
+QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
+ bool *isExtension) const
+{
+ // If the "from" type can hold the target type, we should not try to coerce
+ // it to any constructor argument.
+ if (type.isNull()
+ || canHold(passedArgumentType, type)
+ || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
+ || !type->isCreatable()) {
+ return QQmlJSMetaMethod();
+ }
+
+ auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &type) {
+ QQmlJSMetaMethod candidate;
+
+ const auto ownMethods = type->ownMethods();
+ for (const QQmlJSMetaMethod &method : ownMethods) {
+ if (!method.isConstructor())
+ continue;
+
+ const auto index = method.constructorIndex();
+ Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
+
+ const auto methodArguments = method.parameters();
+ if (methodArguments.size() != 1)
+ continue;
+
+ const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
+
+ if (equals(passedArgumentType, methodArgumentType))
+ return method;
+
+ // Do not select further ctors here. We don't want to do multi-step construction as that
+ // is confusing and easily leads to infinite recursion.
+ if (!candidate.isValid()
+ && canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
+ candidate = method;
+ }
+ }
+
+ return candidate;
+ };
+
+ if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
+ const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
+ if (ctor.isValid()) {
+ if (isExtension)
+ *isExtension = true;
+ return ctor;
+ }
+ }
+
+ if (isExtension)
+ *isExtension = false;
+
+ return doSelectConstructor(type);
+}
+
+bool QQmlJSTypeResolver::areEquivalentLists(
+ const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
+{
+ const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
+ { m_stringListType, m_stringType->listType() },
+ { m_variantListType, m_varType->listType() }
+ };
+
+ for (const auto eq : equivalentLists) {
+ if ((equals(a, eq[0]) && equals(b, eq[1])) || (equals(a, eq[1]) && equals(b, eq[0])))
+ return true;
+ }
+
+ return false;
+}
+
+bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
+{
+ // pointers are trivially copyable
+ if (type->isReferenceType())
+ return true;
+
+ // Enum values are trivially copyable
+ if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return true;
+
+ for (const QQmlJSScope::ConstPtr &trivial : {
+ m_nullType, m_voidType,
+ m_boolType, m_metaObjectType,
+ m_realType, m_floatType,
+ m_int8Type, m_uint8Type,
+ m_int16Type, m_uint16Type,
+ m_int32Type, m_uint32Type,
+ m_int64Type, m_uint64Type }) {
+ if (equals(type, trivial))
+ return true;
+ }
+
+ return false;
+}
+
+bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
+{
+ const bool matchByName = !base->isComposite();
+ for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
+ derivedBase = derivedBase->baseType()) {
+ if (equals(derivedBase, base))
+ return true;
+ if (matchByName
+ && !derivedBase->isComposite()
+ && derivedBase->internalName() == base->internalName()) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
{
@@ -912,12 +1297,14 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
return true;
if (equals(from, m_jsValueType) || equals(to, m_jsValueType))
return true;
+ if (equals(to, m_qQmlScriptStringType))
+ return true;
if (isNumeric(from) && isNumeric(to))
return true;
if (isNumeric(from) && equals(to, m_boolType))
return true;
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
- && equals(to, m_boolType)) {
+ && (equals(to, m_boolType) || equals(to, m_stringType))) {
return true;
}
@@ -925,6 +1312,10 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (isNumeric(from) && equals(to, m_stringType))
return true;
+ // We can convert strings to numbers, but not to enums
+ if (equals(from, m_stringType) && isNumeric(to))
+ return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
+
// We can always convert between strings and urls.
if ((equals(from, m_stringType) && equals(to, m_urlType))
|| (equals(from, m_urlType) && equals(to, m_stringType))) {
@@ -937,25 +1328,26 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
return true;
}
- if (equals(from, m_voidType) || equals(to, m_voidType))
+ if (equals(to, m_voidType))
return true;
if (to.isNull())
- return false;
-
- if (equals(from, m_stringType) && equals(to, m_dateTimeType))
- return true;
+ return equals(from, m_voidType);
- for (const auto &originType : {m_dateTimeType, m_dateType, m_timeType}) {
+ const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
+ for (const auto &originType : types) {
if (!equals(from, originType))
continue;
- for (const auto &targetType : {m_dateTimeType, m_dateType, m_timeType, m_stringType}) {
+ for (const auto &targetType : types) {
if (equals(to, targetType))
return true;
}
- break;;
+ if (equals(to, m_realType))
+ return true;
+
+ break;
}
if (equals(from, m_nullType)
@@ -971,7 +1363,7 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (equals(to, m_jsPrimitiveType))
return isPrimitive(from);
- if (equals(from, m_emptyListType) || equals(from, m_variantListType))
+ if (equals(from, m_variantListType))
return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
const bool matchByName = !to->isComposite();
@@ -987,6 +1379,24 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (canConvertFromTo(from, m_jsPrimitiveType) && canConvertFromTo(m_jsPrimitiveType, to))
return true;
+ // We can convert everything to bool.
+ if (equals(to, m_boolType))
+ return true;
+
+ if (areEquivalentLists(from, to))
+ return true;
+
+ if (from->isListProperty()
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ && canConvertFromTo(from->valueType(), to->valueType())) {
+ return true;
+ }
+
+ if (equals(to, m_stringType)
+ && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ return canConvertFromTo(from->valueType(), m_stringType);
+ }
+
return false;
}
@@ -995,14 +1405,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
{
QQmlJSMetaProperty prop;
prop.setPropertyName(u"length"_s);
- prop.setTypeName(u"int"_s);
- prop.setType(intType());
+ prop.setTypeName(u"qsizetype"_s);
+ prop.setType(sizeType());
prop.setIsWritable(isWritable);
- return QQmlJSRegisterContent::create(intType(), prop, QQmlJSRegisterContent::Builtin, scope);
+ return QQmlJSRegisterContent::create(
+ sizeType(), prop, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin, scope);
}
-QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr &type,
- const QString &name) const
+QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
+ const QQmlJSScope::ConstPtr &type, const QString &name, int baseLookupIndex,
+ int resultLookupIndex) const
{
QQmlJSRegisterContent result;
@@ -1010,14 +1423,26 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (equals(type, metaObjectType()))
return {};
+ if (equals(type, variantMapType())) {
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(name);
+ prop.setTypeName(u"QVariant"_s);
+ prop.setType(varType());
+ prop.setIsWritable(true);
+ return QQmlJSRegisterContent::create(
+ varType(), prop, baseLookupIndex, resultLookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, type);
+ }
+
if (equals(type, jsValueType())) {
QQmlJSMetaProperty prop;
prop.setPropertyName(name);
prop.setTypeName(u"QJSValue"_s);
prop.setType(jsValueType());
prop.setIsWritable(true);
- return QQmlJSRegisterContent::create(jsValueType(), prop,
- QQmlJSRegisterContent::JavaScriptObjectProperty, type);
+ return QQmlJSRegisterContent::create(
+ jsValueType(), prop, baseLookupIndex, resultLookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, type);
}
if ((equals(type, stringType())
@@ -1032,7 +1457,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
const auto prop = scope->ownProperty(name);
result = QQmlJSRegisterContent::create(
storedType(prop.type()),
- prop,
+ prop, baseLookupIndex, resultLookupIndex,
mode == QQmlJSScope::NotExtension
? QQmlJSRegisterContent::ObjectProperty
: QQmlJSRegisterContent::ExtensionObjectProperty,
@@ -1050,20 +1475,6 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
scope);
return true;
}
-
- if (std::optional<QQmlJSScope::JavaScriptIdentifier> identifier =
- scope->findJSIdentifier(name);
- identifier.has_value()) {
- QQmlJSMetaProperty prop;
- prop.setPropertyName(name);
- prop.setTypeName(u"QJSValue"_s);
- prop.setType(jsValueType());
- prop.setIsWritable(!identifier->isConst);
-
- result = QQmlJSRegisterContent::create(
- jsValueType(), prop, QQmlJSRegisterContent::JavaScriptObject, type);
- return true;
- }
}
return checkEnums(scope, name, &result, mode);
@@ -1072,6 +1483,23 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (QQmlJSUtils::searchBaseAndExtensionTypes(type, check))
return result;
+ for (auto scope = type;
+ scope && (scope->scopeType() == QQmlSA::ScopeType::JSFunctionScope
+ || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
+ scope = scope->parentScope()) {
+ if (auto ownIdentifier = scope->ownJSIdentifier(name)) {
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(name);
+ prop.setTypeName(u"QJSValue"_s);
+ prop.setType(jsValueType());
+ prop.setIsWritable(!(ownIdentifier.value().isConst));
+
+ return QQmlJSRegisterContent::create(jsValueType(), prop, baseLookupIndex,
+ resultLookupIndex,
+ QQmlJSRegisterContent::JavaScriptObject, scope);
+ }
+ }
+
if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
if (!genericType(attached)) {
@@ -1085,9 +1513,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
qmlCompiler, type->sourceLocation());
return {};
} else {
- return QQmlJSRegisterContent::create(storedType(attached), attached,
- QQmlJSRegisterContent::ObjectAttached,
- attachedBase);
+ return QQmlJSRegisterContent::create(
+ storedType(attached), attached, resultLookupIndex,
+ QQmlJSRegisterContent::ObjectAttached, attachedBase);
}
}
}
@@ -1110,12 +1538,12 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(const QQmlJSScope::Cons
return {};
}
-QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent &type,
- const QString &name) const
+QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
+ const QQmlJSRegisterContent &type, const QString &name, int lookupIndex) const
{
if (type.isType()) {
const auto content = type.type();
- const auto result = memberType(content, name);
+ const auto result = memberType(content, name, type.resultLookupIndex(), lookupIndex);
if (result.isValid())
return result;
@@ -1124,12 +1552,12 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return memberEnumType(type.scopeType(), name);
}
if (type.isProperty())
- return memberType(type.property().type(), name);
+ return memberType(type.property().type(), name, type.resultLookupIndex(), lookupIndex);
if (type.isEnumeration()) {
const auto enumeration = type.enumeration();
if (!type.enumMember().isEmpty() || !enumeration.hasKey(name))
return {};
- return QQmlJSRegisterContent::create(storedType(intType()), enumeration, name,
+ return QQmlJSRegisterContent::create(storedType(enumeration.type()), enumeration, name,
QQmlJSRegisterContent::ObjectEnum, type.scopeType());
}
if (type.isMethod()) {
@@ -1138,9 +1566,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
prop.setPropertyName(name);
prop.setType(jsValueType());
prop.setIsWritable(true);
- return QQmlJSRegisterContent::create(jsValueType(), prop,
- QQmlJSRegisterContent::JavaScriptObjectProperty,
- jsValueType());
+ return QQmlJSRegisterContent::create(
+ jsValueType(), prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, jsValueType());
}
if (type.isImportNamespace()) {
if (type.scopeType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
@@ -1150,13 +1578,35 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return {};
}
- return referenceTypeForName(
+ return registerContentForName(
name, type.scopeType(),
type.variant() == QQmlJSRegisterContent::ObjectModulePrefix);
}
if (type.isConversion()) {
- const auto result = memberType(type.conversionResult(), name);
- return result.isValid() ? result : memberEnumType(type.scopeType(), name);
+ if (const auto result = memberType(
+ type.conversionResult(), name, type.resultLookupIndex(), lookupIndex);
+ result.isValid()) {
+ return result;
+ }
+
+ if (const auto result = memberEnumType(type.scopeType(), name); result.isValid())
+ return result;
+
+ // If the conversion consists of only undefined and one actual type,
+ // we can produce the members of that one type.
+ // If the value is then actually undefined, the result is an exception.
+
+ auto origins = type.conversionOrigins();
+ const auto begin = origins.begin();
+ const auto end = std::remove_if(begin, origins.end(),
+ [this](const QQmlJSScope::ConstPtr &origin) {
+ return equals(origin, m_voidType);
+ });
+
+ // If the conversion cannot hold the original type, it loses information.
+ return (end - begin == 1 && canHold(type.conversionResult(), *begin))
+ ? memberType(*begin, name, type.resultLookupIndex(), lookupIndex)
+ : QQmlJSRegisterContent();
}
Q_UNREACHABLE_RETURN({});
@@ -1167,13 +1617,22 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
QQmlJSScope::ConstPtr scope;
QQmlJSScope::ConstPtr value;
- auto valueType = [this](const QQmlJSScope::ConstPtr &scope) {
+ auto valueType = [&](const QQmlJSScope::ConstPtr &scope) {
if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return scope->valueType();
- else if (equals(scope, m_jsValueType) || equals(scope, m_varType))
+
+ if (equals(scope, m_forInIteratorPtr))
+ return m_sizeType;
+
+ if (equals(scope, m_forOfIteratorPtr))
+ return list.scopeType()->valueType();
+
+ if (equals(scope, m_jsValueType) || equals(scope, m_varType))
return m_jsValueType;
- else if (equals(scope, m_stringType))
+
+ if (equals(scope, m_stringType))
return m_stringType;
+
return QQmlJSScope::ConstPtr();
};
@@ -1197,7 +1656,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
property.setType(value);
return QQmlJSRegisterContent::create(
- storedType(value), property, QQmlJSRegisterContent::ListValue, scope);
+ storedType(value), property, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue, scope);
}
QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
@@ -1206,7 +1666,32 @@ QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
{
Q_ASSERT(variant == QQmlJSRegisterContent::MethodReturnValue
|| variant == QQmlJSRegisterContent::JavaScriptReturnValue);
- return QQmlJSRegisterContent::create(storedType(type), type, variant, scope);
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex, variant, scope);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
+ const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type,
+ int lookupIndex) const
+{
+ const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
+ ? m_int32Type
+ : containedType(valueType(listType));
+
+ QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
+ ? m_forInIteratorPtr
+ : m_forOfIteratorPtr;
+
+ const QQmlJSScope::ConstPtr listContained = containedType(listType);
+
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(u"<>"_s);
+ prop.setTypeName(iteratorPointer->internalName());
+ prop.setType(iteratorPointer);
+ return QQmlJSRegisterContent::create(
+ storedType(iteratorPointer), prop, lookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
+ listContained);
}
bool QQmlJSTypeResolver::registerIsStoredIn(
@@ -1275,13 +1760,20 @@ QQmlJSRegisterContent QQmlJSTypeResolver::convert(
{
if (from.isConversion()) {
return QQmlJSRegisterContent::create(
- to.storedType(), from.conversionOrigins(), containedType(to), from.variant(),
- from.scopeType());
+ to.storedType(), from.conversionOrigins(), containedType(to),
+ to.scopeType() ? to.scopeType() : from.conversionResultScope(),
+ from.variant(), from.scopeType());
}
return QQmlJSRegisterContent::create(
- to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
- containedType(to), from.variant(), from.scopeType());
+ to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
+ containedType(to), to.scopeType(), from.variant(), from.scopeType());
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::cast(
+ const QQmlJSRegisterContent &from, const QQmlJSScope::ConstPtr &to) const
+{
+ return from.castTo(to).storedIn(storedType(to));
}
QQmlJSScope::ConstPtr QQmlJSTypeResolver::comparableType(const QQmlJSScope::ConstPtr &type) const