diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-02-04 22:35:23 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-02-16 14:44:39 +0100 |
commit | be90de3dc1efc447f17a5fc6ba5f0c6aa760f9a4 (patch) | |
tree | 2a9a0b18bee911769fde02c56700ed88f3393f42 | |
parent | 160190968df4a2b5163700a2aebd88842d0c784e (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.cpp | 175 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscompilepass_p.h | 5 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsfunctioninitializer.cpp | 7 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope.cpp | 9 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope_p.h | 6 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 132 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator_p.h | 4 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 312 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 25 |
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 ®, const QQmlJSScope::ConstPtr &type) const +{ + return equals(reg.storedType(), type); +} + bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent ®, 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 ®, + const QQmlJSScope::ConstPtr &type) const; bool registerContains(const QQmlJSRegisterContent ®, 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 |