aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-02-04 22:35:23 +0100
committerUlf Hermann <ulf.hermann@qt.io>2022-02-16 14:44:39 +0100
commitbe90de3dc1efc447f17a5fc6ba5f0c6aa760f9a4 (patch)
tree2a9a0b18bee911769fde02c56700ed88f3393f42
parent160190968df4a2b5163700a2aebd88842d0c784e (diff)
QmlCompiler: track register contents by cloning types
Whenever we write a register, we create a new type. This way, whenever we determine that a different type would be a better fit at that place, we can just change the type in place, without affecting any unrelated code. Task-number: QTBUG-100157 Change-Id: I70be2eb83bda5483e083f58113fdfe57224b237e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp175
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h5
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp7
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp9
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h6
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp132
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h4
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp312
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h25
9 files changed, 417 insertions, 258 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 8f07baf0c0..d338fbfb66 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -127,6 +127,7 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
for (const auto loopLabel : m_context->labelInfo)
m_labels.insert(loopLabel, u"label_%1"_qs.arg(m_labels.count()));
+ m_state.State::operator=(initialState(function, m_typeResolver));
const QByteArray byteCode = function->code;
decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
eliminateDeadStores();
@@ -139,9 +140,9 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
registerTypeIt != end; ++registerTypeIt) {
const QQmlJSScope::ConstPtr storedType = registerTypeIt.key();
- if (storedType == m_typeResolver->nullType()
- || storedType == m_typeResolver->emptyListType()
- || storedType == m_typeResolver->voidType()) {
+ if (m_typeResolver->equals(storedType, m_typeResolver->nullType())
+ || m_typeResolver->equals(storedType, m_typeResolver->emptyListType())
+ || m_typeResolver->equals(storedType, m_typeResolver->voidType())) {
continue;
}
@@ -388,21 +389,23 @@ void QQmlJSCodeGenerator::generate_Ret()
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs;
if (!m_state.accumulatorVariableIn.isEmpty()) {
const QString in = use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn().storedType() == m_typeResolver->varType()) {
+ if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->varType())) {
m_body += u"if (!"_qs + in + u".isValid())\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn().storedType() == m_typeResolver->jsPrimitiveType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
m_body += u"if ("_qs + in
+ u".type() == QJSPrimitiveValue::Undefined)\n"_qs;
m_body += u" "_qs + signalUndefined;
- } else if (m_state.accumulatorIn().storedType() == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
m_body += u"if ("_qs + in + u".isUndefined())\n"_qs;
m_body += u" "_qs + signalUndefined;
}
m_body += u"return "_qs
+ conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in);
- } else if (m_function->returnType != m_typeResolver->voidType()
- && m_function->returnType->internalName() != u"void"_qs) {
+ } else if (m_function->returnType->internalName() != u"void"_qs) {
if (m_function->returnType->internalName().trimmed().endsWith(u'*')
|| m_function->returnType->internalName().trimmed().endsWith(u'&')) {
setError(u"Not all paths return a value"_qs);
@@ -513,18 +516,19 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
m_jsUnitGenerator->constant(constIndex));
if (v4Value.isNull()) {
- const auto type = m_state.changedRegister().storedType();
+ const auto changed = m_state.changedRegister();
m_body += var + u" = "_qs;
- if (type == m_typeResolver->jsPrimitiveType()) {
+ if (m_typeResolver->registerIsStoredIn(changed, m_typeResolver->jsPrimitiveType())) {
m_body += u"QJSPrimitiveNull()"_qs;
- } else if (type == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(changed, m_typeResolver->jsValueType())) {
m_body += u"QJSValue(QJSValue::NullValue)"_qs;
- } else if (type == m_typeResolver->varType()) {
+ } else if (m_typeResolver->registerIsStoredIn(changed, m_typeResolver->varType())) {
m_body += u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
- } else if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ } else if (changed.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Reference) {
m_body += u"nullptr"_qs;
} else {
- setError(u"Cannot load null into %1"_qs.arg(m_state.changedRegister().descriptiveName()));
+ setError(u"Cannot load null into %1"_qs.arg(changed.descriptiveName()));
}
m_body += u";\n"_qs;
@@ -732,7 +736,7 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
switch (type.variant()) {
case QQmlJSRegisterContent::ScopeProperty:
case QQmlJSRegisterContent::ExtensionScopeProperty: {
- if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn())) {
+ if (!m_typeResolver->registerContains(m_state.accumulatorIn(), type.property().type())) {
m_body += u"{\n"_qs;
m_body += u"auto converted = "_qs
+ conversion(m_state.accumulatorIn(), type, use(m_state.accumulatorVariableIn))
@@ -778,7 +782,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect LoadElement"_qs);
return;
}
@@ -817,7 +821,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect StoreElement"_qs);
return;
}
@@ -951,9 +955,9 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- const auto storedType = m_state.accumulatorIn().storedType();
- const bool isReferenceType
- = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const bool isReferenceType = (accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Reference);
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached) {
if (!isReferenceType) {
// This can happen on incomplete type information. We contextually know that the
@@ -997,16 +1001,17 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
m_body += u"}\n"_qs;
- } else if ((storedType == m_typeResolver->stringType()
- || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType())
+ || accumulatorIn.storedType()->accessSemantics()
+ == QQmlJSScope::AccessSemantics::Sequence)
&& m_jsUnitGenerator->lookupName(index) == u"length"_qs) {
// Special-cased the same way as in QQmlJSTypeResolver::memberType()
m_body += m_state.accumulatorVariableOut + u" = "_qs + use(m_state.accumulatorVariableIn)
+ u".count("_qs;
- if (storedType == m_typeResolver->listPropertyType())
+ if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType()))
m_body += u'&' + use(m_state.accumulatorVariableIn);
m_body += u')' + u";\n"_qs;
- } else if (storedType == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
reject(u"lookup in QJSValue"_qs);
} else {
m_body += u"{\n"_qs;
@@ -1045,10 +1050,10 @@ void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
QString QQmlJSCodeGenerator::setLookupPreparation(
const QQmlJSRegisterContent &content, const QString &arg, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return u"const QMetaType argType = aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u");\n"_qs
+ u"if (argType.isValid())\n "_qs + arg + u".convert(argType)";
@@ -1068,7 +1073,6 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
const QString indexString = QString::number(index);
const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
const QQmlJSRegisterContent callBase = registerType(baseReg);
- const QQmlJSScope::ConstPtr objectType = callBase.storedType();
const QQmlJSRegisterContent specific = m_typeResolver->memberType(
callBase, m_jsUnitGenerator->lookupName(index));
const QQmlJSRegisterContent property = specific.storedIn(
@@ -1080,7 +1084,8 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
QString variableInType;
QString preparation;
QString argType;
- if (property != m_state.accumulatorIn()) {
+ if (!m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->containedType(property))) {
m_body += u"auto converted = "_qs
+ conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
@@ -1097,7 +1102,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
argType = variableInType;
}
- switch (objectType->accessSemantics()) {
+ switch (callBase.storedType()->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference: {
const QString lookup = u"aotContext->setObjectLookup("_qs + indexString
+ u", "_qs + object + u", "_qs + variableIn + u')';
@@ -1113,7 +1118,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
- if (objectType != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) {
reject(u"SetLookup on sequence types (because of missing write-back)"_qs);
break;
}
@@ -1189,7 +1194,7 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
QString args;
QString conversions;
- if (m_typeResolver->containedType(m_state.accumulatorOut()) == m_typeResolver->voidType()) {
+ if (m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->voidType())) {
types = u"QMetaType()"_qs;
args = u"nullptr"_qs;
} else {
@@ -1207,20 +1212,20 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
}
for (int i = 0; i < argc; ++i) {
- const QQmlJSScope::ConstPtr type = registerType(argv + i).storedType();
+ const QQmlJSRegisterContent content = registerType(argv + i);
const QString var = use(registerVariable(argv + i));
- if (type == m_typeResolver->jsPrimitiveType()) {
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
QString argName = u"arg"_qs + QString::number(i);
conversions += u"QVariant "_qs + argName + u" = "_qs
- + conversion(type, m_typeResolver->varType(), var) + u";\n"_qs;
+ + conversion(content.storedType(), m_typeResolver->varType(), var) + u";\n"_qs;
args += u", "_qs + argName + u".data()"_qs;
types += u", "_qs + argName + u".metaType()"_qs;
- } else if (type == m_typeResolver->varType()) {
+ } else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
args += u", "_qs + var + u".data()"_qs;
types += u", "_qs + var + u".metaType()"_qs;
} else {
args += u", &"_qs + var;
- types += u", "_qs + metaTypeFromType(type);
+ types += u", "_qs + metaTypeFromType(content.storedType());
}
}
return conversions
@@ -1280,7 +1285,7 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
addInclude(u"qalgorithms.h"_qs);
addInclude(u"qrandom.h"_qs);
- if (m_state.accumulatorOut().storedType() != m_typeResolver->realType())
+ if (!m_typeResolver->registerIsStoredIn(m_state.accumulatorOut(), m_typeResolver->realType()))
return false;
m_body += u"{\n"_qs;
@@ -1403,7 +1408,7 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
const QString name = m_jsUnitGenerator->stringForIndex(
m_jsUnitGenerator->lookupNameIndex(index));
- if (m_typeResolver->containedType(baseType) == mathObject()) {
+ if (m_typeResolver->registerContains(baseType, mathObject())) {
if (inlineMathMethod(name, argc, argv))
return;
}
@@ -1830,10 +1835,10 @@ void QQmlJSCodeGenerator::generate_CmpNeNull()
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
{
- if (m_state.accumulatorIn().storedType() == m_typeResolver->intType())
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType()))
return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn().storedType() == m_typeResolver->boolType()) {
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) {
return QString::number(lhsConst) + u" == "_qs
+ conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
use(m_state.accumulatorVariableIn));
@@ -1859,10 +1864,10 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
QString QQmlJSCodeGenerator::getLookupPreparation(
const QQmlJSRegisterContent &content, const QString &var, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return var + u" = QVariant(aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u"))"_qs;
}
@@ -1873,9 +1878,9 @@ QString QQmlJSCodeGenerator::getLookupPreparation(
QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored)
+ if (m_typeResolver->registerContains(content, stored))
return u'&' + var;
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType()))
return var + u".data()"_qs;
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u'&' + var;
@@ -1889,9 +1894,9 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
const QQmlJSScope::ConstPtr stored = content.storedType();
const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
m_typeResolver->containedType(content));
- if (contained == stored)
+ if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->equals(stored, m_typeResolver->varType()))
return var + u".metaType()"_qs; // We expect the QVariant to be initialized
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return metaTypeFromName(contained);
@@ -1984,7 +1989,8 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut + u" = "_qs;
- if (m_state.accumulatorIn().storedType() == m_typeResolver->metaObjectType()
+ if (m_typeResolver->equals(
+ m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())
&& contained->isComposite()) {
m_body += conversion(
m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
@@ -2324,7 +2330,7 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto primitive = m_typeResolver->jsPrimitiveType();
- if (lhsType == rhsType && lhsType != primitive) {
+ if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
m_body += use(registerVariable(lhs));
m_body += (invert ? u" != "_qs : u" == "_qs);
m_body += use(m_state.accumulatorVariableIn);
@@ -2400,7 +2406,8 @@ void QQmlJSCodeGenerator::protectAccumulator()
// prepare the output QVariant, and afterwards use the input variant. Therefore we need to move
// the input out of the way first.
if (m_state.accumulatorVariableIn == m_state.accumulatorVariableOut
- && m_state.accumulatorOut().storedType() == m_typeResolver->varType()) {
+ && m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorOut(), m_typeResolver->varType())) {
m_state.accumulatorVariableIn = use(m_state.accumulatorVariableIn) + u"_moved"_qs;
m_body += u"QVariant "_qs + m_state.accumulatorVariableIn
+ u" = std::move("_qs + m_state.accumulatorVariableOut + u");\n"_qs;
@@ -2439,6 +2446,7 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
currentVariable = registerVariable(registerIndex);
}
+ // Actually == here. We want the jump code also for equal types
if (!currentType.isValid() || currentType == targetType)
continue;
Q_ASSERT(m_registerVariables.contains(registerIndex));
@@ -2507,57 +2515,57 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
const auto boolType = m_typeResolver->boolType();
- if (from == m_typeResolver->nullType()) {
+ if (m_typeResolver->equals(from, m_typeResolver->nullType())) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"QJSValue(QJSValue::NullValue)"_qs;
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return u"QJSPrimitiveValue(QJSPrimitiveNull())"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
- if (to == boolType)
+ if (m_typeResolver->equals(to, boolType))
return u"false"_qs;
if (m_typeResolver->isNumeric(m_typeResolver->globalType(to)))
return u"0"_qs;
- if (to == m_typeResolver->stringType())
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return u"null"_qs;
- if (to == from)
+ if (m_typeResolver->equals(from, to))
return QString();
reject(u"Conversion from null to %1"_qs.arg(to->internalName()));
}
- if (from == m_typeResolver->emptyListType()) {
+ if (m_typeResolver->equals(from, m_typeResolver->emptyListType())) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return castTargetName(to) + u"()"_qs;
- if (to == m_typeResolver->varType())
+ if (m_typeResolver->equals(to, m_typeResolver->varType()))
return u"QVariant(QVariantList())"_qs;
- if (to == from)
+ if (m_typeResolver->equals(from, to))
return QString();
reject(u"Conversion from empty list to %1"_qs.arg(to->internalName()));
}
- if (from == to)
+ if (m_typeResolver->equals(from, to))
return variable;
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
// We still have to cast as other execution paths may result in different types.
- if (base == to)
+ if (m_typeResolver->equals(base, to))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
- if (base == from)
+ if (m_typeResolver->equals(base, from))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
- } else if (to == m_typeResolver->boolType()) {
+ } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
return u'(' + variable + u" != nullptr)"_qs;
}
}
auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
- return candidate == jsValueType || candidate->isScript();
+ return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
};
if (isJsValue(from) && isJsValue(to))
@@ -2565,54 +2573,57 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
- || type == m_typeResolver->boolType()
+ || m_typeResolver->equals(type, m_typeResolver->boolType())
|| type->scopeType() == QQmlJSScope::EnumScope;
};
- if (from == m_typeResolver->realType() && to == m_typeResolver->intType())
+ if (m_typeResolver->equals(from, m_typeResolver->realType())
+ && m_typeResolver->equals(to, m_typeResolver->intType())) {
return u"QJSNumberCoercion::toInteger("_qs + variable + u')';
+ }
if (isBoolOrNumber(from) && isBoolOrNumber(to))
return to->internalName() + u'(' + variable + u')';
- if (from == jsPrimitiveType) {
- if (to == m_typeResolver->realType())
+
+ if (m_typeResolver->equals(from, jsPrimitiveType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
return variable + u".toDouble()"_qs;
- if (to == boolType)
+ if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_qs;
- if (to == m_typeResolver->intType())
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
return variable + u".toInteger()"_qs;
- if (to == m_typeResolver->stringType())
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return variable + u".toString()"_qs;
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"QJSValue(QJSPrimitiveValue("_qs + variable + u"))"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant()"_qs;
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs;
}
if (isJsValue(from)) {
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return variable + u".toPrimitive()"_qs;
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return variable + u".toVariant(QJSValue::RetainJSObjects)"_qs;
return u"qjsvalue_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
}
- if (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return u"QJSPrimitiveValue("_qs + variable + u')';
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"aotContext->engine->toScriptValue("_qs + variable + u')';
- if (from == varType) {
- if (to == m_typeResolver->listPropertyType())
+ if (m_typeResolver->equals(from, varType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->listPropertyType()))
return u"QQmlListReference("_qs + variable + u", aotContext->qmlEngine())"_qs;
return u"qvariant_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
}
- if (to == varType)
+ if (m_typeResolver->equals(to, varType))
return u"QVariant::fromValue("_qs + variable + u')';
// TODO: more efficient string conversions, possibly others
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index 3b3c1ee59d..1653c8cf6a 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -109,11 +109,6 @@ public:
m_changedRegisterIndex = registerIndex;
}
- void setAccumulator(QQmlJSRegisterContent content)
- {
- setRegister(Accumulator, std::move(content));
- }
-
void clearChangedRegister()
{
m_changedRegisterIndex = InvalidRegister;
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 7898b25c59..4687c6bc0b 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -92,14 +92,15 @@ void QQmlJSFunctionInitializer::populateSignature(
for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) {
if (argument.typeAnnotation) {
if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) {
- function->argumentTypes.append(type);
+ function->argumentTypes.append(m_typeResolver->tracked(type));
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(
+ m_typeResolver->tracked(m_typeResolver->varType()));
signatureError(u"Cannot resolve the argument type %1."_qs
.arg(argument.typeAnnotation->type->toString()));
}
} else {
- function->argumentTypes.append(m_typeResolver->varType());
+ function->argumentTypes.append(m_typeResolver->tracked(m_typeResolver->varType()));
signatureError(u"Functions without type annotations won't be compiled"_qs);
}
}
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index c01c5d9471..bb7bd58919 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -103,6 +103,15 @@ QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &par
return childScope;
}
+QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
+{
+ if (origin.isNull())
+ return QQmlJSScope::Ptr();
+ QQmlJSScope::Ptr cloned = create(origin->m_scopeType, origin->m_parentScope);
+ *cloned = *origin;
+ return cloned;
+}
+
void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
{
Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope);
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 2cc6555c30..1cf3b6a19f 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -59,7 +59,6 @@ class QQmlJSImporter;
class QQmlJSScope
{
- Q_DISABLE_COPY(QQmlJSScope)
public:
QQmlJSScope(QQmlJSScope &&) = default;
QQmlJSScope &operator=(QQmlJSScope &&) = default;
@@ -181,7 +180,8 @@ public:
};
static QQmlJSScope::Ptr create(ScopeType type = QQmlJSScope::QMLScope,
- const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin);
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
QQmlJSScope::Ptr parentScope()
@@ -462,6 +462,8 @@ public:
private:
QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
+ QQmlJSScope(const QQmlJSScope &) = default;
+ QQmlJSScope &operator=(const QQmlJSScope &) = default;
static ImportedScope<QQmlJSScope::ConstPtr> findType(
const QString &name, const ContextualTypes &contextualTypes,
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index f347c0a46e..cb3c06d310 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -60,6 +60,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_returnType = m_typeResolver->globalType(m_function->returnType);
do {
+ m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
m_state.State::operator=(initialState(m_function, m_typeResolver));
@@ -89,13 +90,12 @@ void QQmlJSTypePropagator::generate_Ret()
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
} else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
- && m_typeResolver->containedType(m_state.accumulatorIn())
- != m_typeResolver->voidType()) {
+ && !m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->voidType())) {
setError(u"function without type annotation returns %1"_qs
.arg(m_state.accumulatorIn().descriptiveName()));
return;
- } else if (m_state.accumulatorIn() != m_returnType
- && !canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
+ } else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
setError(u"cannot convert from %1 to %2"_qs
.arg(m_state.accumulatorIn().descriptiveName(),
m_returnType.descriptiveName()));
@@ -118,60 +118,59 @@ void QQmlJSTypePropagator::generate_Debug()
void QQmlJSTypePropagator::generate_LoadConst(int index)
{
auto encodedConst = m_jsUnitGenerator->constant(index);
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadZero()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadFalse()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
}
void QQmlJSTypePropagator::generate_LoadUndefined()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
}
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
{
auto encodedConst = m_jsUnitGenerator->constant(constIndex);
- m_state.setRegister(
- destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
+ setRegister(destTemp, m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadReg(int reg)
{
- m_state.setAccumulator(checkedInputRegister(reg));
+ setAccumulator(checkedInputRegister(reg));
}
void QQmlJSTypePropagator::generate_StoreReg(int reg)
{
- m_state.setRegister(reg, m_state.accumulatorIn());
+ setRegister(reg, m_state.accumulatorIn());
}
void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
{
Q_ASSERT(destReg != InvalidRegister);
- m_state.setRegister(destReg, m_state.registers[srcReg]);
+ setRegister(destReg, m_state.registers[srcReg]);
}
void QQmlJSTypePropagator::generate_LoadImport(int index)
@@ -183,7 +182,7 @@ void QQmlJSTypePropagator::generate_LoadImport(int index)
void QQmlJSTypePropagator::generate_LoadLocal(int index)
{
Q_UNUSED(index);
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_StoreLocal(int index)
@@ -209,7 +208,7 @@ void QQmlJSTypePropagator::generate_StoreScopedLocal(int scope, int index)
void QQmlJSTypePropagator::generate_LoadRuntimeString(int stringId)
{
Q_UNUSED(stringId)
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
// m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
}
@@ -229,7 +228,7 @@ void QQmlJSTypePropagator::generate_LoadClosure(int value)
void QQmlJSTypePropagator::generate_LoadName(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
if (!m_state.accumulatorOut().isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -444,11 +443,11 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
const QQmlJSRegisterContent inType = m_typeResolver->globalType(m_function->qmlScope);
- m_state.setAccumulator(QQmlJSRegisterContent::create(
+ setAccumulator(QQmlJSRegisterContent::create(
inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
m_typeResolver->containedType(inType)));
return;
@@ -507,11 +506,11 @@ void QQmlJSTypePropagator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseRegister = m_state.registers[base];
if (!m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->intType())
|| baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
return;
}
- m_state.setAccumulator(m_typeResolver->valueType(baseRegister));
+ setAccumulator(m_typeResolver->valueType(baseRegister));
}
void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
@@ -522,7 +521,7 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{
- m_state.setAccumulator(
+ setAccumulator(
m_typeResolver->memberType(
m_state.accumulatorIn(),
m_state.accumulatorIn().isImportNamespace()
@@ -578,7 +577,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
Q_ASSERT(m_state.accumulatorIn().isValid());
- m_state.setAccumulator(QQmlJSRegisterContent::create(
+ setAccumulator(QQmlJSRegisterContent::create(
m_state.accumulatorIn().storedType(),
m_jsUnitGenerator->getStringId(propertyName),
QQmlJSRegisterContent::ObjectModulePrefix,
@@ -591,7 +590,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
&& m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
m_logger->log(u"Cannot load singleton as property of object"_qs, Log_Type,
getCurrentSourceLocation());
- m_state.setAccumulator(QQmlJSRegisterContent());
+ setAccumulator(QQmlJSRegisterContent());
}
bool isRestricted = checkRestricted(propertyName);
@@ -773,17 +772,16 @@ void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, i
void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
{
- auto callBase = m_state.registers[base];
+ const auto callBase = m_state.registers[base];
const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
- const auto member = m_typeResolver->memberType(callBase, propertyName);
- const auto containedType = m_typeResolver->containedType(callBase);
- if (containedType == m_typeResolver->jsValueType()
- || containedType == m_typeResolver->varType()) {
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
+ || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
return;
}
+ const auto member = m_typeResolver->memberType(callBase, propertyName);
if (!member.isMethod()) {
setError(u"Type %1 does not have a property %2 for calling"_qs
.arg(callBase.descriptiveName(), propertyName));
@@ -809,7 +807,7 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
return;
}
- checkDeprecated(containedType, propertyName, true);
+ checkDeprecated(m_typeResolver->containedType(callBase), propertyName, true);
propagateCall(member.method(), argc, argv);
}
@@ -867,6 +865,26 @@ QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMe
return javascriptFunction;
}
+void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
+{
+ setRegister(Accumulator, content);
+}
+
+void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
+{
+ // If we've come to the same conclusion before, let's not track the type again.
+ auto it = m_prevStateAnnotations.constFind(currentInstructionOffset());
+ if (it != m_prevStateAnnotations.constEnd()) {
+ const QQmlJSRegisterContent &lastTry = it->changedRegister;
+ if (m_typeResolver->registerContains(lastTry, m_typeResolver->containedType(content))) {
+ m_state.setRegister(index, lastTry);
+ return;
+ }
+ }
+
+ m_state.setRegister(index, m_typeResolver->tracked(content));
+}
+
void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
{
QStringList errors;
@@ -882,7 +900,7 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods,
}
const auto returnType = match.returnType();
- m_state.setAccumulator(m_typeResolver->globalType(
+ setAccumulator(m_typeResolver->globalType(
returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType()));
if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_qs.arg(match.methodName()));
@@ -928,7 +946,7 @@ void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName,
}
setError(u"method %1 cannot be resolved."_qs.arg(functionName));
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
setError(u"Cannot find function '%1'"_qs.arg(functionName));
handleUnqualifiedAccess(functionName);
@@ -971,7 +989,7 @@ void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
Q_UNUSED(argc)
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
@@ -1008,7 +1026,7 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
- m_state.setAccumulator(QQmlJSRegisterContent());
+ setAccumulator(QQmlJSRegisterContent());
}
void QQmlJSTypePropagator::generate_GetException()
@@ -1110,12 +1128,12 @@ void QQmlJSTypePropagator::generate_DeleteName(int name)
void QQmlJSTypePropagator::generate_TypeofName(int name)
{
Q_UNUSED(name);
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_TypeofValue()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
@@ -1128,7 +1146,7 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
Q_UNUSED(args);
- m_state.setAccumulator(m_typeResolver->globalType(argc == 0
+ setAccumulator(m_typeResolver->globalType(argc == 0
? m_typeResolver->emptyListType()
: m_typeResolver->jsValueType()));
}
@@ -1140,7 +1158,7 @@ void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int
Q_UNUSED(internalClassId)
Q_UNUSED(argc)
Q_UNUSED(args)
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1227,18 +1245,18 @@ void QQmlJSTypePropagator::generate_CheckException()
void QQmlJSTypePropagator::generate_CmpEqNull()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpNeNull()
{
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
{
Q_UNUSED(lhsConst)
- m_state.setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
m_state.accumulatorIn())));
}
@@ -1246,7 +1264,7 @@ void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
{
Q_UNUSED(lhsConst)
- m_state.setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
m_state.accumulatorIn())));
}
@@ -1323,7 +1341,7 @@ void QQmlJSTypePropagator::generate_As(int lhs)
setError(u"invalid cast from %1 to %2. You can only cast object types."_qs
.arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
} else {
- m_state.setAccumulator(m_typeResolver->globalType(contained));
+ setAccumulator(m_typeResolver->globalType(contained));
}
}
@@ -1335,18 +1353,18 @@ void QQmlJSTypePropagator::generate_UNot()
.arg(m_state.accumulatorIn().descriptiveName()));
return;
}
- m_state.setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_UPlus()
{
- m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ setAccumulator(m_typeResolver->typeForUnaryOperation(
QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_UMinus()
{
- m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ setAccumulator(m_typeResolver->typeForUnaryOperation(
QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn()));
}
@@ -1357,13 +1375,13 @@ void QQmlJSTypePropagator::generate_UCompl()
void QQmlJSTypePropagator::generate_Increment()
{
- m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ setAccumulator(m_typeResolver->typeForUnaryOperation(
QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_Decrement()
{
- m_state.setAccumulator(m_typeResolver->typeForUnaryOperation(
+ setAccumulator(m_typeResolver->typeForUnaryOperation(
QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn()));
}
@@ -1399,14 +1417,14 @@ void QQmlJSTypePropagator::generate_UShr(int lhs)
void QQmlJSTypePropagator::generate_Shr(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::generate_Shl(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn()));
}
@@ -1438,7 +1456,7 @@ void QQmlJSTypePropagator::generate_ShrConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::RShift, m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->intType())));
}
@@ -1447,7 +1465,7 @@ void QQmlJSTypePropagator::generate_ShlConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.setAccumulator(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::LShift, m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->intType())));
}
@@ -1612,8 +1630,8 @@ void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
if (!lhsRegister.isValid())
return;
- m_state.setAccumulator(
- m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn()));
+ setAccumulator(m_typeResolver->typeForBinaryOperation(
+ op, lhsRegister, m_state.accumulatorIn()));
}
void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index f8f8e4c53d..08eeb6f09e 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -227,12 +227,16 @@ private:
QQmlJSMetaMethod bestMatchForCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
QStringList *errors);
+ void setAccumulator(const QQmlJSRegisterContent &content);
+ void setRegister(int index, const QQmlJSRegisterContent &content);
+
QQmlJSRegisterContent m_returnType;
QQmlJSTypeInfo *m_typeInfo = nullptr;
// Not part of the state, as the back jumps are the reason for running multiple passes
QMultiHash<int, ExpectedRegisterState> m_jumpOriginRegisterStateByTargetInstructionOffset;
+ InstructionAnnotations m_prevStateAnnotations;
PassState m_state;
};
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index b4dcb668ad..caa717f0fc 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -64,6 +64,7 @@ static bool searchBaseAndExtensionTypes(const QQmlJSScope::ConstPtr type, const
}
QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
+ : m_typeTracker(std::make_unique<TypeTracker>())
{
const QQmlJSImporter::ImportedTypes builtinTypes = importer->builtinInternalNames();
m_voidType = builtinTypes[u"void"_qs].scope;
@@ -150,10 +151,10 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
}
if (!canConvertFromTo(binding.literalType(), property.type())) {
m_logger->log(u"Cannot assign binding of type %1 to %2"_qs
- .arg(binding.literalTypeName())
- .arg(property.typeName()),
+ .arg(binding.literalTypeName(), property.typeName()),
Log_Type, binding.sourceLocation());
- } else if (property.type() == m_stringType && isNumeric(binding.literalType())) {
+ } else if (equals(property.type(), m_stringType)
+ && isNumeric(binding.literalType())) {
m_logger->log(u"Cannot assign a numeric constant to a string property"_qs,
Log_Type, binding.sourceLocation());
}
@@ -178,21 +179,22 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &elementType) const
{
+ auto it = m_typeTracker->listTypes.find(elementType);
+ if (it != m_typeTracker->listTypes.end())
+ return *it;
+
switch (elementType->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference:
return m_listPropertyType;
case QQmlJSScope::AccessSemantics::Value: {
- auto it = m_listTypes.find(elementType);
- if (it != m_listTypes.end())
- return *it;
QQmlJSScope::Ptr listType = QQmlJSScope::create();
listType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
listType->setValueTypeName(elementType->internalName());
listType->setInternalName(u"QList<%1>"_qs.arg(elementType->internalName()));
listType->setFileName(elementType->fileName());
QQmlJSScope::resolveTypes(listType, m_imports);
- Q_ASSERT(listType->valueType() == elementType);
- m_listTypes[elementType] = listType;
+ Q_ASSERT(equals(listType->valueType(), elementType));
+ m_typeTracker->listTypes[elementType] = listType;
return listType;
}
default:
@@ -255,12 +257,12 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::Add: {
const auto leftContents = containedType(left);
const auto rightContents = containedType(right);
- if (leftContents == stringType() || rightContents == stringType())
+ if (equals(leftContents, stringType()) || equals(rightContents, stringType()))
return QQmlJSRegisterContent::create(stringType(), stringType(),
QQmlJSRegisterContent::Builtin);
const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -272,7 +274,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
}
case QSOperator::Op::Sub: {
const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right));
- if (result == boolType())
+ if (equals(result, boolType()))
return QQmlJSRegisterContent::create(intType(), intType(),
QQmlJSRegisterContent::Builtin);
if (isNumeric(result))
@@ -322,16 +324,16 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
- return type == m_intType || type == m_realType || type == m_floatType || type == m_boolType
- || type == m_voidType || type == m_nullType || type == m_stringType
- || type == m_jsPrimitiveType;
+ return equals(type, m_intType) || equals(type, m_realType) || equals(type, m_floatType)
+ || equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType)
+ || equals(type, m_stringType) || equals(type, m_jsPrimitiveType);
}
bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
{
return searchBaseAndExtensionTypes(
type, [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension) {
- return scope == m_numberPrototype;
+ return equals(scope, m_numberPrototype);
});
}
@@ -342,7 +344,7 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
return container.type();
if (container.isProperty()) {
const QQmlJSMetaProperty prop = container.property();
- return prop.isList() ? listPropertyType() : QQmlJSScope::ConstPtr(prop.type());
+ return prop.isList() ? listType(prop.type()) : QQmlJSScope::ConstPtr(prop.type());
}
if (container.isEnumeration())
return container.enumeration().type();
@@ -357,6 +359,74 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
return {};
}
+void QQmlJSTypeResolver::trackListPropertyType(
+ const QQmlJSScope::ConstPtr &trackedListElementType) const
+{
+ Q_ASSERT(m_typeTracker->trackedTypes.contains(trackedListElementType));
+ if (!m_typeTracker->listTypes.contains(trackedListElementType)) {
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(m_listPropertyType);
+ m_typeTracker->listTypes[trackedListElementType] = clone;
+ m_typeTracker->trackedTypes[clone] = { m_listPropertyType, clone };
+ }
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::tracked(const QQmlJSScope::ConstPtr &origin) const
+{
+ const auto it = m_typeTracker->trackedTypes.find(origin);
+ QQmlJSScope::ConstPtr type = (it == m_typeTracker->trackedTypes.end()) ? origin : it->original;
+
+ QQmlJSScope::Ptr clone = QQmlJSScope::clone(type);
+ m_typeTracker->trackedTypes[clone] = { std::move(type), clone };
+ return clone;
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::tracked(const QQmlJSRegisterContent &origin) const
+{
+ if (origin.isType()) {
+ return QQmlJSRegisterContent::create(
+ origin.storedType(), tracked(origin.type()),
+ origin.variant(), origin.scopeType());
+ }
+
+ if (origin.isProperty()) {
+ QQmlJSMetaProperty prop = origin.property();
+ prop.setType(tracked(prop.type()));
+ if (prop.isList())
+ trackListPropertyType(prop.type());
+ return QQmlJSRegisterContent::create(
+ origin.storedType(), prop,
+ origin.variant(), origin.scopeType());
+ }
+
+ if (origin.isEnumeration()) {
+ QQmlJSMetaEnum enumeration = origin.enumeration();
+ enumeration.setType(tracked(enumeration.type()));
+ return QQmlJSRegisterContent::create(
+ origin.storedType(), enumeration, origin.enumMember(),
+ origin.variant(), origin.scopeType());
+ }
+
+ // We can't track methods
+ if (origin.isMethod())
+ return origin;
+
+ if (origin.isImportNamespace()) {
+ return QQmlJSRegisterContent::create(
+ origin.storedType(), origin.importNamespace(),
+ origin.variant(), tracked(origin.scopeType()));
+ }
+
+ if (origin.isConversion()) {
+ return QQmlJSRegisterContent::create(
+ origin.storedType(), origin.conversionOrigins(),
+ tracked(origin.conversionResult()),
+ origin.variant(), origin.scopeType());
+ }
+
+ Q_UNREACHABLE();
+ return {};
+}
+
QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &container) const
{
QQmlJSScope::ConstPtr type;
@@ -379,35 +449,39 @@ bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
// ### need a generic solution for custom cpp types:
- // if (from->m_hasBoolOverload && to == boolType)
+ // if (from->m_hasBoolOverload && equals(to, boolType))
// return true;
- if (from == to)
+ if (equals(from, to))
return true;
- if (from == m_varType || to == m_varType)
+ if (equals(from, m_varType) || equals(to, m_varType))
return true;
- if (from == m_jsValueType || to == m_jsValueType)
+ if (equals(from, m_jsValueType) || equals(to, m_jsValueType))
return true;
if (isNumeric(from) && isNumeric(to))
return true;
- if (from == m_intType && to == m_boolType)
+ if (equals(from, m_intType) && equals(to, m_boolType))
return true;
- if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference && to == m_boolType)
+ if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
+ && equals(to, m_boolType)) {
return true;
+ }
// Yes, our String has number constructors.
- if (isNumeric(from) && to == m_stringType)
+ if (isNumeric(from) && equals(to, m_stringType))
return true;
// We can always convert between strings and urls.
- if ((from == m_stringType && to == m_urlType) || (from == m_urlType && to == m_stringType))
+ if ((equals(from, m_stringType) && equals(to, m_urlType))
+ || (equals(from, m_urlType) && equals(to, m_stringType))) {
return true;
+ }
// All of these types have QString conversions that require a certain format
// TODO: Actually verify these strings or deprecate them
- if (from == m_stringType && !to.isNull()) {
+ if (equals(from, m_stringType) && !to.isNull()) {
const QString toTypeName = to->internalName();
- if (to == m_dateTimeType || toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
+ if (equals(to, m_dateTimeType) || toTypeName == u"QTime"_qs || toTypeName == u"QDate"_qs
|| toTypeName == u"QPoint"_qs || toTypeName == u"QPointF"_qs
|| toTypeName == u"QSize"_qs || toTypeName == u"QSizeF"_qs || toTypeName == u"QRect"_qs
|| toTypeName == u"QRectF"_qs || toTypeName == u"QColor"_qs) {
@@ -415,30 +489,32 @@ bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
}
}
- if (from == m_voidType)
+ if (equals(from, m_voidType))
return true;
if (to.isNull())
return false;
- if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(from, m_nullType)
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
return true;
+ }
- if (from == m_jsPrimitiveType) {
+ if (equals(from, m_jsPrimitiveType)) {
// You can cast any primitive to a nullptr
return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
}
- if (to == m_jsPrimitiveType)
+ if (equals(to, m_jsPrimitiveType))
return isPrimitive(from);
- if (from == m_emptyListType)
+ if (equals(from, m_emptyListType))
return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
const bool matchByName = !to->isComposite();
Q_ASSERT(!matchByName || !to->internalName().isEmpty());
for (auto baseType = from; baseType; baseType = baseType->baseType()) {
- if (baseType == to)
+ if (equals(baseType, to))
return true;
if (matchByName && baseType->internalName() == to->internalName())
return true;
@@ -485,32 +561,32 @@ QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
merge(a.scopeType(), b.scopeType()));
}
-static QQmlJSScope::ConstPtr commonBaseType(const QQmlJSScope::ConstPtr &a,
- const QQmlJSScope::ConstPtr &b)
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
+ const QQmlJSScope::ConstPtr &b) const
{
- for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
- for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
- if (aBase == bBase)
- return aBase;
+ const auto commonBaseType = [this](
+ const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
+ for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
+ for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
+ if (equals(aBase, bBase))
+ return aBase;
+ }
}
- }
- return {};
-}
+ return QQmlJSScope::ConstPtr();
+ };
-QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
- const QQmlJSScope::ConstPtr &b) const
-{
- if (a == b)
+
+ if (equals(a, b))
return a;
- if (a == jsValueType() || a == varType())
+ if (equals(a, jsValueType()) || equals(a, varType()))
return a;
- if (b == jsValueType() || b == varType())
+ if (equals(b, jsValueType()) || equals(b, varType()))
return b;
auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) {
- return (a == from && b == to) || (b == from && a == to);
+ return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to));
};
if (isNumeric(a) && isNumeric(b))
@@ -526,10 +602,10 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (auto commonBase = commonBaseType(a, b))
return commonBase;
- if (a == nullType() && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(a, nullType()) && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return b;
- if (b == nullType() && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if (equals(b, nullType()) && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return a;
return varType();
@@ -541,7 +617,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
if (type->isScript())
return m_jsValueType;
- if (type == m_metaObjectType)
+ if (equals(type, m_metaObjectType))
return m_metaObjectType;
if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
@@ -568,12 +644,13 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt
return m_jsValueType;
}
- if (type == voidType())
+ if (equals(type, voidType()))
return jsPrimitiveType();
- if (isPrimitive(type) || type == m_jsValueType || type == m_listPropertyType
- || type == m_urlType || type == m_dateTimeType || type == m_variantListType
- || type == m_varType || type == m_stringListType || type == m_emptyListType) {
+ if (isPrimitive(type) || equals(type, m_jsValueType) || equals(type, m_listPropertyType)
+ || equals(type, m_urlType) || equals(type, m_dateTimeType)
+ || equals(type, m_variantListType) || equals(type, m_varType)
+ || equals(type, m_stringListType) || equals(type, m_emptyListType)) {
return type;
}
@@ -609,46 +686,6 @@ scopeContentVariant(QQmlJSTypeResolver::BaseOrExtension mode, bool isMethod)
return QQmlJSRegisterContent::Unknown;
}
-static bool isAssignedToDefaultProperty(const QQmlJSScope::ConstPtr &parent,
- const QQmlJSScope::ConstPtr &child)
-{
- QString defaultPropertyName;
- QQmlJSMetaProperty defaultProperty;
- if (!searchBaseAndExtensionTypes(
- parent, [&](const QQmlJSScope::ConstPtr &scope,
- QQmlJSTypeResolver::BaseOrExtension mode) {
- Q_UNUSED(mode);
- defaultPropertyName = scope->defaultPropertyName();
- defaultProperty = scope->property(defaultPropertyName);
- return !defaultPropertyName.isEmpty();
- })) {
- return false;
- }
-
- QQmlJSScope::ConstPtr bindingHolder = parent;
- while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
- // Only traverse the base type hierarchy here, not the extended types.
- // Extensions cannot hold bindings.
- bindingHolder = bindingHolder->baseType();
-
- // Consequently, the default property may be inaccessibly
- // hidden in some extension via shadowing.
- // Nothing can be assigned to it then.
- if (!bindingHolder)
- return false;
- }
-
- const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
- = bindingHolder->propertyBindings(defaultPropertyName);
- for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
- && binding.objectType() == child) {
- return true;
- }
- }
- return false;
-}
-
static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isComposite());
@@ -669,6 +706,46 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s
QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
const QString &name) const
{
+ const auto isAssignedToDefaultProperty = [this](
+ const QQmlJSScope::ConstPtr &parent, const QQmlJSScope::ConstPtr &child) {
+ QString defaultPropertyName;
+ QQmlJSMetaProperty defaultProperty;
+ if (!searchBaseAndExtensionTypes(
+ parent, [&](const QQmlJSScope::ConstPtr &scope,
+ QQmlJSTypeResolver::BaseOrExtension mode) {
+ Q_UNUSED(mode);
+ defaultPropertyName = scope->defaultPropertyName();
+ defaultProperty = scope->property(defaultPropertyName);
+ return !defaultPropertyName.isEmpty();
+ })) {
+ return false;
+ }
+
+ QQmlJSScope::ConstPtr bindingHolder = parent;
+ while (bindingHolder->property(defaultPropertyName) != defaultProperty) {
+ // Only traverse the base type hierarchy here, not the extended types.
+ // Extensions cannot hold bindings.
+ bindingHolder = bindingHolder->baseType();
+
+ // Consequently, the default property may be inaccessibly
+ // hidden in some extension via shadowing.
+ // Nothing can be assigned to it then.
+ if (!bindingHolder)
+ return false;
+ }
+
+ const QList<QQmlJSMetaPropertyBinding> defaultPropBindings
+ = bindingHolder->propertyBindings(defaultPropertyName);
+ for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
+ if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
+ && equals(binding.objectType(), child)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
if (QQmlJSScope::ConstPtr identified = scopeForId(name, scope)) {
return QQmlJSRegisterContent::create(storedType(identified), identified,
QQmlJSRegisterContent::ObjectById, scope);
@@ -818,7 +895,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
{
QQmlJSRegisterContent result;
- if (type == jsValueType()) {
+ if (equals(type, jsValueType())) {
QQmlJSMetaProperty prop;
prop.setPropertyName(name);
prop.setTypeName(u"QJSValue"_qs);
@@ -828,9 +905,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
QQmlJSRegisterContent::JavaScriptObjectProperty, type);
}
- if ((type == stringType() || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ if ((equals(type, stringType())
+ || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
&& name == u"length"_qs) {
- return lengthProperty(type != stringType(), type);
+ return lengthProperty(!equals(type, stringType()), type);
}
const auto check = [&](const QQmlJSScope::ConstPtr &scope, BaseOrExtension mode) {
@@ -991,7 +1069,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
auto valueType = [this](const QQmlJSScope::ConstPtr &scope) {
if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return scope->valueType();
- else if (scope == m_jsValueType || scope == m_varType)
+ else if (equals(scope, m_jsValueType) || equals(scope, m_varType))
return m_jsValueType;
return QQmlJSScope::ConstPtr();
};
@@ -1031,21 +1109,27 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
return QQmlJSRegisterContent::create(stored, property, QQmlJSRegisterContent::ListValue, scope);
}
+bool QQmlJSTypeResolver::registerIsStoredIn(
+ const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(reg.storedType(), type);
+}
+
bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const
{
if (reg.isType())
- return reg.type() == type;
+ return equals(reg.type(), type);
if (reg.isConversion())
- return reg.conversionResult() == type;
+ return equals(reg.conversionResult(), type);
if (reg.isProperty()) {
const auto prop = reg.property();
- return prop.isList() ? type == listPropertyType() : prop.type() == type;
+ return prop.isList() ? equals(type, listPropertyType()) : equals(prop.type(), type);
}
if (reg.isEnumeration())
- return type == intType();
+ return equals(type, intType());
if (reg.isMethod())
- return type == jsValueType();
+ return equals(type, jsValueType());
return false;
}
@@ -1053,7 +1137,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
{
if (type.isNull())
return {};
- if (type == voidType())
+ if (equals(type, voidType()))
return jsPrimitiveType();
if (type->isScript())
return jsValueType();
@@ -1069,4 +1153,22 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr
return type;
}
+/*!
+ * \internal
+ *
+ * Compares the origin types of \a a and \a b. A straight a == b would compare the identity
+ * of the pointers. However, since we clone types to keep track of them, we need a separate
+ * way to compare the clones. Usually you'd do *a == *b for that, but as QQmlJSScope is rather
+ * large, we offer an optimization here that uses the type tracking we already have in place.
+ */
+bool QQmlJSTypeResolver::equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
+{
+ auto originalType = [this](const QQmlJSScope::ConstPtr &type) {
+ const auto it = m_typeTracker->trackedTypes.constFind(type);
+ return it == m_typeTracker->trackedTypes.constEnd() ? type : it->original;
+ };
+
+ return originalType(a) == originalType(b);
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 538648d3de..30088636cf 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -120,16 +120,19 @@ public:
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const;
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &listType) const;
+ bool registerIsStoredIn(const QQmlJSRegisterContent &reg,
+ const QQmlJSScope::ConstPtr &type) const;
bool registerContains(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const;
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const;
QString containedTypeName(const QQmlJSRegisterContent &container) const;
+ QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &origin) const;
void setParentMode(ParentMode mode) { m_parentMode = mode; }
ParentMode parentMode() const { return m_parentMode; }
- QQmlJSScope::ConstPtr
- storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr storedType(const QQmlJSScope::ConstPtr &type) const;
+ QQmlJSScope::ConstPtr tracked(const QQmlJSScope::ConstPtr &origin) const;
const QQmlJSScopesById &objectsById() const { return m_objectsById; }
const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers() const
@@ -137,6 +140,8 @@ public:
return m_signalHandlers;
}
+ bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const;
+
protected:
QQmlJSScope::ConstPtr merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const;
@@ -149,6 +154,7 @@ protected:
bool checkEnums(const QQmlJSScope::ConstPtr &scope, const QString &name,
QQmlJSRegisterContent *result, BaseOrExtension mode) const;
QQmlJSRegisterContent lengthProperty(bool isWritable, const QQmlJSScope::ConstPtr &scope) const;
+ void trackListPropertyType(const QQmlJSScope::ConstPtr &trackedListElementType) const;
QQmlJSScope::ConstPtr m_voidType;
QQmlJSScope::ConstPtr m_emptyListType;
@@ -178,8 +184,19 @@ protected:
ParentMode m_parentMode = UseParentProperty;
QQmlJSLogger *m_logger = nullptr;
- // This needs to be mutable as it's a cache. We create the list types on demand.
- mutable QHash<QQmlJSScope::ConstPtr, QQmlJSScope::Ptr> m_listTypes;
+ struct TrackedType
+ {
+ QQmlJSScope::ConstPtr original;
+ QQmlJSScope::Ptr clone;
+ };
+
+ struct TypeTracker
+ {
+ QHash<QQmlJSScope::ConstPtr, QQmlJSScope::Ptr> listTypes;
+ QHash<QQmlJSScope::ConstPtr, TrackedType> trackedTypes;
+ };
+
+ std::unique_ptr<TypeTracker> m_typeTracker;
};
QT_END_NAMESPACE