aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljstypepropagator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljstypepropagator.cpp')
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp994
1 files changed, 685 insertions, 309 deletions
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 6072393e0f..2760f3401b 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -60,8 +60,13 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_returnType = m_typeResolver->globalType(m_function->returnType);
do {
+ // Reset the error if we need to do another pass
+ if (m_state.needsMorePasses)
+ *m_error = QQmlJS::DiagnosticMessage();
+
+ m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
- m_state.State::operator=(initialState(m_function, m_typeResolver));
+ m_state.State::operator=(initialState(m_function));
reset();
decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
@@ -69,7 +74,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
// If we have found unresolved backwards jumps, we need to start over with a fresh state.
// Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case.
// This means that we won't start over for the same reason again.
- } while (!m_error->isValid() && m_state.needsMorePasses);
+ } while (m_state.needsMorePasses);
return m_state.annotations;
}
@@ -80,34 +85,41 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
return;
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
- m_logger->logWarning( \
- u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
- Log_Compiler); \
+ m_logger->log(u"Instruction \"%1\" not implemented"_qs.arg(QString::fromUtf8(__func__)), \
+ Log_Compiler, QQmlJS::SourceLocation()); \
return;
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()) {
+ } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
+ && !m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->voidType())) {
setError(u"function without type annotation returns %1"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .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(),
+ .arg(m_state.accumulatorIn().descriptiveName(),
m_returnType.descriptiveName()));
- m_logger->logWarning(u"Cannot assign binding of type %1 to %2"_qs.arg(
- m_typeResolver->containedTypeName(m_state.accumulatorIn),
- m_typeResolver->containedTypeName(m_returnType)),
- Log_Type, getCurrentBindingSourceLocation());
+ m_logger->log(u"Cannot assign binding of type %1 to %2"_qs.arg(
+ m_typeResolver->containedTypeName(m_state.accumulatorIn()),
+ m_typeResolver->containedTypeName(m_returnType)),
+ Log_Type, getCurrentBindingSourceLocation());
return;
}
+ if (m_returnType.isValid()) {
+ // We need to preserve any possible undefined value as that resets the property.
+ if (m_typeResolver->canHoldUndefined(m_state.accumulatorIn()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_returnType);
+ }
+
+ m_state.setHasSideEffects(true);
m_state.skipInstructionsUntilNextJumpTarget = true;
}
@@ -119,37 +131,37 @@ void QQmlJSTypePropagator::generate_Debug()
void QQmlJSTypePropagator::generate_LoadConst(int index)
{
auto encodedConst = m_jsUnitGenerator->constant(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->typeForConst(encodedConst)));
}
void QQmlJSTypePropagator::generate_LoadZero()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadFalse()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->nullType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->nullType()));
}
void QQmlJSTypePropagator::generate_LoadUndefined()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->voidType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->voidType()));
}
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->intType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
@@ -160,17 +172,24 @@ void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
void QQmlJSTypePropagator::generate_LoadReg(int reg)
{
- m_state.accumulatorOut = checkedInputRegister(reg);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(Accumulator, checkedInputRegister(reg));
}
void QQmlJSTypePropagator::generate_StoreReg(int reg)
{
- setRegister(reg, m_state.accumulatorIn);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(reg, m_state.accumulatorIn());
}
void QQmlJSTypePropagator::generate_MoveReg(int srcReg, int destReg)
{
- setRegister(destReg, m_state.registers[srcReg]);
+ Q_ASSERT(destReg != InvalidRegister);
+ // Do not re-track the register. We're not manipulating it.
+ m_state.setIsRename(true);
+ m_state.setRegister(destReg, m_state.registers[srcReg]);
}
void QQmlJSTypePropagator::generate_LoadImport(int index)
@@ -182,7 +201,7 @@ void QQmlJSTypePropagator::generate_LoadImport(int index)
void QQmlJSTypePropagator::generate_LoadLocal(int index)
{
Q_UNUSED(index);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_StoreLocal(int index)
@@ -208,7 +227,7 @@ void QQmlJSTypePropagator::generate_StoreScopedLocal(int scope, int index)
void QQmlJSTypePropagator::generate_LoadRuntimeString(int stringId)
{
Q_UNUSED(stringId)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
// m_state.accumulatorOut.m_state.value = m_jsUnitGenerator->stringForIndex(stringId);
}
@@ -228,8 +247,8 @@ void QQmlJSTypePropagator::generate_LoadClosure(int value)
void QQmlJSTypePropagator::generate_LoadName(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(m_function->qmlScope, name);
- if (!m_state.accumulatorOut.isValid())
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -260,7 +279,7 @@ QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() c
return combine(entries.constFirst().location, entries.constLast().location);
}
-void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
+void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isMethod) const
{
auto location = getCurrentSourceLocation();
@@ -271,6 +290,13 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
return;
}
+ if (isMethod) {
+ if (isCallingProperty(m_function->qmlScope, name))
+ return;
+ } else if (isMissingPropertyType(m_function->qmlScope, name)) {
+ return;
+ }
+
std::optional<FixSuggestion> suggestion;
auto childScopes = m_function->qmlScope->childScopes();
@@ -297,7 +323,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
const auto handler = m_typeResolver->signalHandlers()[id.location];
- QString fixString = handler.isMultiline ? u" function("_qs : u" ("_qs;
+ QString fixString = handler.isMultiline ? u"function("_qs : u"("_qs;
const auto parameters = handler.signalParameters;
for (int numParams = parameters.size(); numParams > 0; --numParams) {
fixString += parameters.at(parameters.size() - numParams);
@@ -314,7 +340,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
"function instead.\n")
.arg(id.location.startLine)
.arg(id.location.startColumn),
- fixLocation, fixString
+ fixLocation, fixString, QString(), false
};
}
break;
@@ -334,7 +360,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
name + QLatin1String(" is a member of a parent element\n")
+ QLatin1String(" You can qualify the access with its id "
"to avoid this warning:\n"),
- fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.'))
+ fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.')), QString(), id.isEmpty()
};
if (id.isEmpty()) {
@@ -358,8 +384,8 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
}
}
- m_logger->logWarning(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true,
- true, suggestion);
+ m_logger->log(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location, true, true,
+ suggestion);
}
void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name,
@@ -407,65 +433,122 @@ void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QS
if (!deprecation.reason.isEmpty())
message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
- m_logger->logWarning(message, Log_Deprecation, getCurrentSourceLocation());
+ m_logger->log(message, Log_Deprecation, getCurrentSourceLocation());
}
-bool QQmlJSTypePropagator::checkRestricted(const QString &propertyName) const
+bool QQmlJSTypePropagator::isRestricted(const QString &propertyName) const
{
QString restrictedKind;
- if (!m_state.accumulatorIn.isValid())
+ const auto accumulatorIn = m_state.registers.find(Accumulator);
+ if (accumulatorIn == m_state.registers.end())
return false;
- if (m_state.accumulatorIn.isList() && propertyName != u"length") {
+ if (accumulatorIn.value().isList() && propertyName != u"length") {
restrictedKind = u"a list"_qs;
- } else if (m_state.accumulatorIn.isEnumeration()
- && !m_state.accumulatorIn.enumeration().hasKey(propertyName)) {
+ } else if (accumulatorIn.value().isEnumeration()
+ && !accumulatorIn.value().enumeration().hasKey(propertyName)) {
restrictedKind = u"an enum"_qs;
- } else if (m_state.accumulatorIn.isMethod()) {
+ } else if (accumulatorIn.value().isMethod()) {
restrictedKind = u"a method"_qs;
}
if (!restrictedKind.isEmpty())
- m_logger->logWarning(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(
- restrictedKind, propertyName),
- Log_Type, getCurrentSourceLocation());
+ m_logger->log(u"Type is %1. You cannot access \"%2\" from here."_qs.arg(restrictedKind,
+ propertyName),
+ Log_Type, getCurrentSourceLocation());
return !restrictedKind.isEmpty();
}
+// Only to be called once a lookup has already failed
+bool QQmlJSTypePropagator::isMissingPropertyType(QQmlJSScope::ConstPtr scope,
+ const QString &propertyName) const
+{
+ auto property = scope->property(propertyName);
+ if (!property.isValid())
+ return false;
+
+ QString errorType;
+ if (property.type().isNull())
+ errorType = u"found"_qs;
+ else if (!property.type()->isFullyResolved())
+ errorType = u"fully resolved"_qs;
+
+ Q_ASSERT(!errorType.isEmpty());
+
+ m_logger->log(
+ u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
+ .arg(property.typeName(), propertyName, errorType),
+ Log_Type, getCurrentSourceLocation());
+
+ return true;
+}
+
+bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const
+{
+ auto property = scope->property(name);
+ if (!property.isValid())
+ return false;
+
+ QString propertyType = u"Property"_qs;
+
+ auto methods = scope->methods(name);
+
+ QString errorType;
+ if (!methods.isEmpty()) {
+ errorType = u"shadowed by a property."_qs;
+ switch (methods.first().methodType()) {
+ case QQmlJSMetaMethod::Signal:
+ propertyType = u"Signal"_qs;
+ break;
+ case QQmlJSMetaMethod::Slot:
+ propertyType = u"Slot"_qs;
+ break;
+ case QQmlJSMetaMethod::Method:
+ propertyType = u"Method"_qs;
+ break;
+ }
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
+ errorType =
+ u"a variant property. It may or may not be a method. Use a regular function instead."_qs;
+ } else if (m_typeResolver->equals(property.type(), m_typeResolver->jsValueType())) {
+ errorType =
+ u"a QJSValue property. It may or may not be a method. Use a regular Q_INVOKABLE instead."_qs;
+ } else {
+ errorType = u"not a method"_qs;
+ }
+
+ m_logger->log(u"%1 \"%2\" is %3"_qs.arg(propertyType, name, errorType), Log_Type,
+ getCurrentSourceLocation(), true, true, {});
+
+ return true;
+}
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
+ // LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
+ // Any import namespaces etc. are handled via LoadProperty or GetLookup.
+
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- m_state.accumulatorOut = m_typeResolver->scopedType(
- m_function->qmlScope,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
- + u'.' + name
- : name);
-
- if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
- const QQmlJSRegisterContent inType = m_state.accumulatorIn.isValid()
- ? m_state.accumulatorIn
- : m_typeResolver->globalType(m_function->qmlScope);
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
- m_typeResolver->containedType(inType));
+ 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);
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_typeResolver->voidType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
+ m_typeResolver->containedType(inType)));
return;
}
checkDeprecated(m_function->qmlScope, name, false);
- bool isRestricted = checkRestricted(name);
-
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot access value for name "_qs + name);
-
- if (!isRestricted)
- handleUnqualifiedAccess(name);
- } else if (m_typeResolver->genericType(m_state.accumulatorOut.storedType()).isNull()) {
+ handleUnqualifiedAccess(name, false);
+ } else if (m_typeResolver->genericType(m_state.accumulatorOut().storedType()).isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
setError(u"Cannot determine generic type for "_qs + name);
@@ -490,20 +573,24 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
if (!type.isWritable() && !m_function->qmlScope->hasOwnProperty(name)) {
setError(u"Can't assign to read-only property %1"_qs.arg(name));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
- getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(name), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, type)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), type)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), type.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), type.descriptiveName()));
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
@@ -511,41 +598,76 @@ void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
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.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ setAccumulator(jsValue);
return;
}
- m_state.accumulatorOut = m_typeResolver->valueType(baseRegister);
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->intType()))
+ addReadAccumulator(m_state.accumulatorIn());
+ else
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ setAccumulator(m_typeResolver->valueType(baseRegister));
}
void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
{
- Q_UNUSED(base)
- Q_UNUSED(index)
+ const QQmlJSRegisterContent baseRegister = m_state.registers[base];
+ const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
+
+ if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ || !m_typeResolver->isNumeric(indexRegister)) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ addReadRegister(base, jsValue);
+ addReadRegister(index, jsValue);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(indexRegister, m_typeResolver->intType()))
+ addReadRegister(index, indexRegister);
+ else
+ addReadRegister(index, m_typeResolver->globalType(m_typeResolver->realType()));
+
+ addReadRegister(base, baseRegister);
+ addReadAccumulator(m_typeResolver->valueType(baseRegister));
+
+ // If we're writing a QQmlListProperty backed by a container somewhere else,
+ // that has side effects.
+ // If we're writing to a list retrieved from a property, that _should_ have side effects,
+ // but currently the QML engine doesn't implement them.
+ // TODO: Figure out the above and accurately set the flag.
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{
- m_state.accumulatorOut =
+ setAccumulator(
m_typeResolver->memberType(
- m_state.accumulatorIn,
- m_state.accumulatorIn.isImportNamespace()
- ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
+ m_state.accumulatorIn(),
+ m_state.accumulatorIn().isImportNamespace()
+ ? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
+ u'.' + propertyName
- : propertyName);
+ : propertyName));
if (m_typeInfo != nullptr
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ScopeAttached) {
- QQmlJSScope::ConstPtr attachedType = m_state.accumulatorIn.scopeType();
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ScopeAttached) {
+ QQmlJSScope::ConstPtr attachedType = m_typeResolver->originalType(
+ m_state.accumulatorIn().scopeType());
for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
scope = scope->parentScope()) {
if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
// Ignore enum accesses, as these will not cause the attached object to be created
- if (m_state.accumulatorOut.isValid() && m_state.accumulatorOut.isEnumeration())
+ if (m_state.accumulatorOut().isValid() && m_state.accumulatorOut().isEnumeration())
continue;
const QString id = m_function->addressableScopes.id(scope);
@@ -555,11 +677,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
fixLocation.length = 0;
- suggestion.fixes << FixSuggestion::Fix {
- u"Reference it by id instead:"_qs,
- fixLocation,
- id.isEmpty() ? u"<id>."_qs : (id + u'.')
- };
+ suggestion.fixes << FixSuggestion::Fix { u"Reference it by id instead:"_qs,
+ fixLocation,
+ id.isEmpty() ? u"<id>."_qs : (id + u'.'),
+ QString(), id.isEmpty() };
fixLocation = scope->sourceLocation();
fixLocation.length = 0;
@@ -571,9 +692,9 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
{} };
}
- m_logger->logWarning(
+ m_logger->log(
u"Using attached type %1 already initialized in a parent scope."_qs.arg(
- m_state.accumulatorIn.scopeType()->internalName()),
+ m_state.accumulatorIn().scopeType()->internalName()),
Log_AttachedPropertyReuse, getCurrentSourceLocation(), true, true,
suggestion);
}
@@ -581,61 +702,47 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
}
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
- Q_ASSERT(m_state.accumulatorIn.isValid());
- m_state.accumulatorOut = QQmlJSRegisterContent::create(
- m_state.accumulatorIn.storedType(),
+ Q_ASSERT(m_state.accumulatorIn().isValid());
+ addReadAccumulator(m_state.accumulatorIn());
+ setAccumulator(QQmlJSRegisterContent::create(
+ m_state.accumulatorIn().storedType(),
m_jsUnitGenerator->getStringId(propertyName),
QQmlJSRegisterContent::ObjectModulePrefix,
- m_typeResolver->containedType(m_state.accumulatorIn));
+ m_typeResolver->containedType(m_state.accumulatorIn())));
return;
}
- if (m_state.accumulatorIn.isImportNamespace())
- m_logger->logWarning(u"Type not found in namespace"_qs, Log_Type,
- getCurrentSourceLocation());
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton
- && m_state.accumulatorIn.variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
- m_logger->logWarning(u"Cannot load singleton as property of object"_qs, Log_Type,
- getCurrentSourceLocation());
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ if (m_state.accumulatorIn().isImportNamespace())
+ m_logger->log(u"Type not found in namespace"_qs, Log_Type, getCurrentSourceLocation());
+ } else if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::Singleton
+ && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectModulePrefix) {
+ m_logger->log(u"Cannot load singleton as property of object"_qs, Log_Type,
+ getCurrentSourceLocation());
+ setAccumulator(QQmlJSRegisterContent());
}
- bool isRestricted = checkRestricted(propertyName);
+ const bool isRestrictedProperty = isRestricted(propertyName);
- if (!m_state.accumulatorOut.isValid()) {
+ if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot load property %1 from %2."_qs
- .arg(propertyName, m_state.accumulatorIn.descriptiveName()));
+ .arg(propertyName, m_state.accumulatorIn().descriptiveName()));
- if (isRestricted)
+ if (isRestrictedProperty)
return;
- const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn);
+ const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn());
if (typeName == u"QVariant")
return;
- if (m_state.accumulatorIn.isList() && propertyName == u"length")
+ if (m_state.accumulatorIn().isList() && propertyName == u"length")
return;
- auto baseType = m_typeResolver->containedType(m_state.accumulatorIn);
+ auto baseType = m_typeResolver->containedType(m_state.accumulatorIn());
// Warn separately when a property is only not found because of a missing type
- if (auto property = baseType->property(propertyName); property.isValid()) {
-
- QString errorType;
- if (property.type().isNull())
- errorType = u"found"_qs;
- else if (!property.type()->isFullyResolved())
- errorType = u"fully resolved"_qs;
-
- Q_ASSERT(!errorType.isEmpty());
-
- m_logger->logWarning(
- u"Type \"%1\" of property \"%2\" not %3. This is likely due to a missing dependency entry or a type not being exposed declaratively."_qs
- .arg(property.typeName(), propertyName, errorType),
- Log_Type, getCurrentSourceLocation());
+ if (isMissingPropertyType(baseType, propertyName))
return;
- }
std::optional<FixSuggestion> fixSuggestion;
@@ -650,30 +757,45 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
}
- m_logger->logWarning(
+ m_logger->log(
u"Property \"%1\" not found on type \"%2\""_qs.arg(propertyName).arg(typeName),
Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- if (m_state.accumulatorOut.isMethod() && m_state.accumulatorOut.method().length() != 1) {
+ if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().length() != 1) {
setError(u"Cannot determine overloaded method on loadProperty"_qs);
return;
}
- if (m_state.accumulatorOut.isProperty()) {
- if (m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->voidType())) {
+ if (m_state.accumulatorOut().isProperty()) {
+ if (m_typeResolver->registerContains(
+ m_state.accumulatorOut(), m_typeResolver->voidType())) {
setError(u"Type %1 does not have a property %2 for reading"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), propertyName));
+ .arg(m_state.accumulatorIn().descriptiveName(), propertyName));
return;
}
- if (!m_state.accumulatorOut.property().type()) {
- m_logger->logWarning(
- QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
- Log_Type, getCurrentSourceLocation());
+ if (!m_state.accumulatorOut().property().type()) {
+ m_logger->log(
+ QString::fromLatin1("Type of property \"%2\" not found").arg(propertyName),
+ Log_Type, getCurrentSourceLocation());
}
}
+
+ switch (m_state.accumulatorOut().variant()) {
+ case QQmlJSRegisterContent::ObjectEnum:
+ case QQmlJSRegisterContent::ExtensionObjectEnum:
+ case QQmlJSRegisterContent::Singleton:
+ // For reading enums or singletons, we don't need to access anything, unless it's an
+ // import namespace. Then we need the name.
+ if (m_state.accumulatorIn().isImportNamespace())
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ default:
+ addReadAccumulator(m_state.accumulatorIn());
+ break;
+ }
}
void QQmlJSTypePropagator::generate_LoadProperty(int nameIndex)
@@ -715,17 +837,21 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
if (!property.isWritable()) {
setError(u"Can't assign to read-only property %1"_qs.arg(propertyName));
- m_logger->logWarning(u"Cannot assign to read-only property %1"_qs.arg(propertyName),
- Log_Property, getCurrentSourceLocation());
+ m_logger->log(u"Cannot assign to read-only property %1"_qs.arg(propertyName), Log_Property,
+ getCurrentSourceLocation());
return;
}
- if (!canConvertFromTo(m_state.accumulatorIn, property)) {
+ if (!canConvertFromTo(m_state.accumulatorIn(), property)) {
setError(u"cannot convert from %1 to %2"_qs
- .arg(m_state.accumulatorIn.descriptiveName(), property.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName(), property.descriptiveName()));
return;
}
+
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(property);
+ addReadRegister(base, callBase);
}
void QQmlJSTypePropagator::generate_SetLookup(int index, int base)
@@ -762,6 +888,7 @@ void QQmlJSTypePropagator::generate_Resume(int)
void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -770,6 +897,7 @@ void QQmlJSTypePropagator::generate_CallValue(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(name)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -779,22 +907,47 @@ 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];
+ Q_ASSERT(m_state.registers.contains(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.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ if (m_typeResolver->registerContains(
+ callBase, m_typeResolver->jsGlobalObject()->property(u"Math"_qs).type())) {
+
+ // If we call a method on the Math object we don't need the actual Math object. We do need
+ // to transfer the type information to the code generator so that it knows that this is the
+ // Math object. Read the base register as void. void isn't stored, and the place where it's
+ // created will be optimized out if there are no other readers. The code generator can
+ // retrieve the original type and determine that it was the Math object.
+ addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
+
+ QQmlJSRegisterContent realType = m_typeResolver->globalType(m_typeResolver->realType());
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, realType);
+ setAccumulator(realType);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
+ || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
+ const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(base, jsValueType);
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, jsValueType);
+ setAccumulator(jsValueType);
+ m_state.setHasSideEffects(true);
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));
- if (checkRestricted(propertyName))
+ if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
+ return;
+
+ if (isRestricted(propertyName))
return;
std::optional<FixSuggestion> fixSuggestion;
@@ -809,14 +962,15 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
}
}
- m_logger->logWarning(u"Property \"%1\" not found on type \"%2\""_qs.arg(
- propertyName, m_typeResolver->containedTypeName(callBase)),
- Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
+ m_logger->log(u"Property \"%1\" not found on type \"%2\""_qs.arg(
+ propertyName, m_typeResolver->containedTypeName(callBase)),
+ Log_Type, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- checkDeprecated(containedType, propertyName, true);
+ checkDeprecated(m_typeResolver->containedType(callBase), propertyName, true);
+ addReadRegister(base, callBase);
propagateCall(member.method(), argc, argv);
}
@@ -873,6 +1027,72 @@ 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.find(currentInstructionOffset());
+ if (it != m_prevStateAnnotations.end()) {
+ const QQmlJSRegisterContent &lastTry = it->second.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::mergeRegister(
+ int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
+{
+ auto merged = m_typeResolver->merge(a, b);
+
+ Q_ASSERT(merged.isValid());
+ Q_ASSERT(merged.isConversion());
+
+ auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
+ auto it = m_prevStateAnnotations.find(currentInstructionOffset());
+ if (it == m_prevStateAnnotations.end())
+ return false;
+
+ auto conversion = it->second.typeConversions.find(index);
+ if (conversion == it->second.typeConversions.end())
+ return false;
+
+ const QQmlJSRegisterContent &lastTry = conversion.value();
+
+ Q_ASSERT(lastTry.isValid());
+ Q_ASSERT(lastTry.isConversion());
+
+ if (!m_typeResolver->equals(lastTry.conversionResult(), merged.conversionResult())
+ || lastTry.conversionOrigins() != merged.conversionOrigins()) {
+ return false;
+ }
+
+ // We don't need to track it again if we've come to the same conclusion before.
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
+ m_state.registers[index] = lastTry;
+ return true;
+ };
+
+ if (!tryPrevStateConversion(index, merged)) {
+ merged = m_typeResolver->tracked(merged);
+ Q_ASSERT(merged.isValid());
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = merged;
+ m_state.registers[index] = merged;
+ }
+}
+
+void QQmlJSTypePropagator::addReadRegister(int index, const QQmlJSRegisterContent &convertTo)
+{
+ m_state.addReadRegister(index, m_typeResolver->convert(m_state.registers[index], convertTo));
+}
+
void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv)
{
QStringList errors;
@@ -887,11 +1107,30 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods,
return;
}
- const auto returnType = match.returnType();
- m_state.accumulatorOut = m_typeResolver->globalType(
- returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType());
- if (!m_state.accumulatorOut.isValid())
+ const auto returnType = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(match.returnType());
+ setAccumulator(m_typeResolver->returnType(
+ returnType ? QQmlJSScope::ConstPtr(returnType) : m_typeResolver->voidType(),
+ match.isJavaScriptFunction() ? QQmlJSRegisterContent::JavaScriptReturnValue
+ : QQmlJSRegisterContent::MethodReturnValue));
+ if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_qs.arg(match.methodName()));
+
+ m_state.setHasSideEffects(true);
+ const auto types = match.parameterTypes();
+ for (int i = 0; i < argc; ++i) {
+ if (i < types.length()) {
+ const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction()
+ ? m_typeResolver->jsValueType()
+ : QQmlJSScope::ConstPtr(types.at(i));
+ if (!type.isNull()) {
+ addReadRegister(argv + i, m_typeResolver->globalType(type));
+ continue;
+ }
+ }
+ addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ }
}
void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base, int argc,
@@ -902,6 +1141,7 @@ void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base
void QQmlJSTypePropagator::generate_CallElement(int base, int index, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(base)
Q_UNUSED(index)
Q_UNUSED(argc)
@@ -916,6 +1156,7 @@ void QQmlJSTypePropagator::generate_CallName(int name, int argc, int argv)
void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(argc)
Q_UNUSED(argv)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -934,10 +1175,11 @@ void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName,
}
setError(u"method %1 cannot be resolved."_qs.arg(functionName));
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
setError(u"Cannot find function '%1'"_qs.arg(functionName));
- handleUnqualifiedAccess(functionName);
+
+ handleUnqualifiedAccess(functionName, true);
}
void QQmlJSTypePropagator::generate_CallGlobalLookup(int index, int argc, int argv)
@@ -954,6 +1196,7 @@ void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(int index, int
void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -963,6 +1206,7 @@ void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int
void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(thisObject)
Q_UNUSED(argc)
@@ -972,16 +1216,18 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argv)
Q_UNUSED(argc)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int argv)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(func)
Q_UNUSED(argc)
Q_UNUSED(argv)
@@ -990,17 +1236,20 @@ void QQmlJSTypePropagator::generate_ConstructWithSpread(int func, int argc, int
void QQmlJSTypePropagator::generate_SetUnwindHandler(int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindDispatch()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
}
void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(level)
Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
@@ -1014,7 +1263,9 @@ void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
void QQmlJSTypePropagator::generate_ThrowException()
{
- m_state.accumulatorOut = QQmlJSRegisterContent();
+ setAccumulator(QQmlJSRegisterContent());
+ m_state.setHasSideEffects(true);
+ m_state.skipInstructionsUntilNextJumpTarget = true;
}
void QQmlJSTypePropagator::generate_GetException()
@@ -1024,16 +1275,18 @@ void QQmlJSTypePropagator::generate_GetException()
void QQmlJSTypePropagator::generate_SetException()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CreateCallContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
Q_UNUSED(name)
INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE();
@@ -1041,34 +1294,39 @@ void QQmlJSTypePropagator::generate_PushCatchContext(int index, int name)
void QQmlJSTypePropagator::generate_PushWithContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushBlockContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_CloneBlockContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PushScriptContext(int index)
{
+ m_state.setHasSideEffects(true);
Q_UNUSED(index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopScriptContext()
{
+ m_state.setHasSideEffects(true);
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
void QQmlJSTypePropagator::generate_PopContext()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
@@ -1118,12 +1376,12 @@ void QQmlJSTypePropagator::generate_DeleteName(int name)
void QQmlJSTypePropagator::generate_TypeofName(int name)
{
Q_UNUSED(name);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_TypeofValue()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->stringType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->stringType()));
}
void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
@@ -1135,9 +1393,10 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(argc == 0
+ ? m_typeResolver->emptyListType()
+ : m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1147,7 +1406,7 @@ void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int
Q_UNUSED(internalClassId)
Q_UNUSED(argc)
Q_UNUSED(args)
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1192,36 +1451,40 @@ void QQmlJSTypePropagator::generate_ToObject()
void QQmlJSTypePropagator::generate_Jump(int offset)
{
saveRegisterStateForJump(offset);
- m_state.accumulatorIn = QQmlJSRegisterContent();
- m_state.accumulatorOut = QQmlJSRegisterContent();
m_state.skipInstructionsUntilNextJumpTarget = true;
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpTrue(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpFalse(int offset)
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_JumpNoException(int offset)
{
saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
@@ -1232,77 +1495,174 @@ void QQmlJSTypePropagator::generate_JumpNotUndefined(int offset)
void QQmlJSTypePropagator::generate_CheckException()
{
- m_state.accumulatorOut = m_state.accumulatorIn;
+ m_state.setHasSideEffects(true);
+}
+
+void QQmlJSTypePropagator::recordEqualsNullType()
+{
+ // TODO: We can specialize this further, for QVariant, QJSValue, int, bool, whatever.
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->nullType())
+ || m_typeResolver->containedType(m_state.accumulatorIn())->isReferenceType()) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsIntType()
+{
+ // We have specializations for numeric types and bool.
+ const QQmlJSScope::ConstPtr in = m_typeResolver->containedType(m_state.accumulatorIn());
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), m_typeResolver->boolType())
+ || m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+}
+void QQmlJSTypePropagator::recordEqualsType(int lhs)
+{
+ const auto isNumericOrEnum = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration() || m_typeResolver->isNumeric(content);
+ };
+
+ const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) {
+ return content.isEnumeration()
+ || m_typeResolver->registerContains(content, m_typeResolver->intType());
+ };
+
+ const auto accumulatorIn = m_state.accumulatorIn();
+ const auto lhsRegister = m_state.registers[lhs];
+
+ // If the types are primitive, we compare directly ...
+ if (m_typeResolver->isPrimitive(accumulatorIn)) {
+ if (m_typeResolver->registerContains(
+ accumulatorIn, m_typeResolver->containedType(lhsRegister))) {
+ addReadRegister(lhs, accumulatorIn);
+ addReadAccumulator(accumulatorIn);
+ return;
+ } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) {
+ const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister)
+ ? m_typeResolver->globalType(m_typeResolver->intType())
+ : m_typeResolver->globalType(m_typeResolver->realType());
+ addReadRegister(lhs, targetType);
+ addReadAccumulator(targetType);
+ return;
+ } else if (m_typeResolver->isPrimitive(lhsRegister)) {
+ const QQmlJSRegisterContent primitive = m_typeResolver->globalType(
+ m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, primitive);
+ addReadAccumulator(primitive);
+ }
+ }
+
+ // Otherwise they're both casted to QJSValue.
+ // TODO: We can add more specializations here: void/void null/null object/null etc
+
+ const QQmlJSRegisterContent jsval = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(lhs, jsval);
+ addReadAccumulator(jsval);
+}
+
+void QQmlJSTypePropagator::recordCompareType(int lhs)
+{
+ // If they're both numeric, we can compare them directly.
+ // They may be casted to double, though.
+ const QQmlJSRegisterContent read
+ = (m_typeResolver->isNumeric(m_state.accumulatorIn())
+ && m_typeResolver->isNumeric(m_state.registers[lhs]))
+ ? m_typeResolver->merge(m_state.accumulatorIn(), m_state.registers[lhs])
+ : m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+ addReadRegister(lhs, read);
+ addReadAccumulator(read);
}
void QQmlJSTypePropagator::generate_CmpEqNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpNeNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ recordEqualsNullType();
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
}
void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
{
+ recordEqualsIntType();
Q_UNUSED(lhsConst)
- m_state.accumulatorOut = QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
+ setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorIn));
+ m_state.accumulatorIn())));
}
void QQmlJSTypePropagator::generate_CmpEq(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::Equal, lhs);
}
void QQmlJSTypePropagator::generate_CmpNe(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::NotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpGt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Gt, lhs);
}
void QQmlJSTypePropagator::generate_CmpGe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Ge, lhs);
}
void QQmlJSTypePropagator::generate_CmpLt(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Lt, lhs);
}
void QQmlJSTypePropagator::generate_CmpLe(int lhs)
{
+ recordCompareType(lhs);
propagateBinaryOperation(QSOperator::Op::Le, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpStrictNotEqual(int lhs)
{
+ recordEqualsType(lhs);
propagateBinaryOperation(QSOperator::Op::StrictNotEqual, lhs);
}
void QQmlJSTypePropagator::generate_CmpIn(int lhs)
{
+ // TODO: Most of the time we don't need the object at all, but only its metatype.
+ // Fix this when we add support for the "in" instruction to the code generator.
+ // Also, specialize on lhs to avoid conversion to QJSPrimitiveValue.
+
+ addReadRegister(lhs, m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+
propagateBinaryOperation(QSOperator::Op::In, lhs);
}
@@ -1317,47 +1677,59 @@ void QQmlJSTypePropagator::generate_As(int lhs)
const QQmlJSRegisterContent input = checkedInputRegister(lhs);
QQmlJSScope::ConstPtr contained;
- switch (m_state.accumulatorIn.variant()) {
+ switch (m_state.accumulatorIn().variant()) {
case QQmlJSRegisterContent::ScopeAttached:
+ contained = m_state.accumulatorIn().scopeType();
+ break;
case QQmlJSRegisterContent::MetaType:
- contained = m_state.accumulatorIn.scopeType();
+ contained = m_state.accumulatorIn().scopeType();
+ if (contained->isComposite()) // Otherwise we don't need it
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
break;
default:
- contained = m_typeResolver->containedType(m_state.accumulatorIn);
+ contained = m_typeResolver->containedType(m_state.accumulatorIn());
break;
}
+ addReadRegister(lhs, m_typeResolver->globalType(contained));
+
if (m_typeResolver->containedType(input)->accessSemantics()
!= QQmlJSScope::AccessSemantics::Reference
|| contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
setError(u"invalid cast from %1 to %2. You can only cast object types."_qs
- .arg(input.descriptiveName(), m_state.accumulatorIn.descriptiveName()));
+ .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
} else {
- m_state.accumulatorOut = m_typeResolver->globalType(contained);
+ setAccumulator(m_typeResolver->globalType(contained));
}
}
void QQmlJSTypePropagator::generate_UNot()
{
- if (!canConvertFromTo(m_state.accumulatorIn,
+ if (!canConvertFromTo(m_state.accumulatorIn(),
m_typeResolver->globalType(m_typeResolver->boolType()))) {
setError(u"cannot convert from %1 to boolean"_qs
- .arg(m_state.accumulatorIn.descriptiveName()));
+ .arg(m_state.accumulatorIn().descriptiveName()));
return;
}
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->boolType());
+ const QQmlJSRegisterContent boolType = m_typeResolver->globalType(m_typeResolver->boolType());
+ addReadAccumulator(boolType);
+ setAccumulator(boolType);
}
void QQmlJSTypePropagator::generate_UPlus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Plus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UMinus()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Minus, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_UCompl()
@@ -1367,19 +1739,25 @@ void QQmlJSTypePropagator::generate_UCompl()
void QQmlJSTypePropagator::generate_Increment()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Increment, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Decrement()
{
- m_state.accumulatorOut = m_typeResolver->typeForUnaryOperation(
- QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForArithmeticUnaryOperation(
+ QQmlJSTypeResolver::UnaryOperator::Decrement, m_state.accumulatorIn());
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Add(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Add, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAnd(int lhs)
@@ -1409,15 +1787,21 @@ void QQmlJSTypePropagator::generate_UShr(int lhs)
void QQmlJSTypePropagator::generate_Shr(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Shl(int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, lhsRegister, m_state.accumulatorIn());
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_BitAndConst(int rhs)
@@ -1448,18 +1832,22 @@ void QQmlJSTypePropagator::generate_ShrConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::RShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::RShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_ShlConst(int rhsConst)
{
Q_UNUSED(rhsConst)
- m_state.accumulatorOut = m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::LShift, m_state.accumulatorIn,
- m_typeResolver->globalType(m_typeResolver->intType()));
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ QSOperator::Op::LShift, m_state.accumulatorIn(),
+ m_typeResolver->globalType(m_typeResolver->intType()));
+ addReadAccumulator(type);
+ setAccumulator(type);
}
void QQmlJSTypePropagator::generate_Exp(int lhs)
@@ -1470,22 +1858,30 @@ void QQmlJSTypePropagator::generate_Exp(int lhs)
void QQmlJSTypePropagator::generate_Mul(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mul, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Div(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Div, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Mod(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Mod, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_Sub(int lhs)
{
- propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ const auto type = propagateBinaryOperation(QSOperator::Op::Sub, lhs);
+ addReadRegister(lhs, type);
+ addReadAccumulator(type);
}
void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
@@ -1506,45 +1902,53 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
+static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+{
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
QV4::Moth::ByteCodeHandler::Verdict
-QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
+QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
{
if (m_error->isValid())
return SkipInstruction;
- if (m_state.jumpTargets.contains(currentInstructionOffset()))
- m_state.skipInstructionsUntilNextJumpTarget = false;
- else if (m_state.skipInstructionsUntilNextJumpTarget)
+ if (m_state.jumpTargets.contains(currentInstructionOffset())) {
+ if (m_state.skipInstructionsUntilNextJumpTarget) {
+ // When re-surfacing from dead code, all registers are invalid.
+ m_state.registers.clear();
+ m_state.skipInstructionsUntilNextJumpTarget = false;
+ }
+ } else if (m_state.skipInstructionsUntilNextJumpTarget
+ && !instructionManipulatesContext(type)) {
return SkipInstruction;
-
- bool instructionWritesAccumulatorWithoutReading = false;
- switch (instr) {
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadZero:
- case QV4::Moth::Instr::Type::LoadTrue:
- case QV4::Moth::Instr::Type::LoadFalse:
- case QV4::Moth::Instr::Type::LoadConst:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadUndefined:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadLocal:
- case QV4::Moth::Instr::Type::LoadQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::LoadGlobalLookup:
- case QV4::Moth::Instr::Type::CallQmlContextPropertyLookup:
- case QV4::Moth::Instr::Type::CallGlobalLookup:
- case QV4::Moth::Instr::Type::CallPropertyLookup:
- instructionWritesAccumulatorWithoutReading = true;
- break;
- default:
- break;
}
const int currentOffset = currentInstructionOffset();
// If we reach an instruction that is a target of a jump earlier, then we must check that the
// register state at the origin matches the current state. If not, then we may have to inject
- // conversion code (communicated to code gen via m_state.expectedTargetTypesBeforeJump). For
+ // conversion code (communicated to code gen via m_state.typeConversions). For
// example:
//
// function blah(x: number) { return x > 10 ? 10 : x}
@@ -1563,11 +1967,6 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
registerIt != end; ++registerIt) {
const int registerIndex = registerIt.key();
- // AccumulatorOut will be the "in" for this (upcoming) instruction, so if that one
- // merely writes to it, we don't care about it's value at the origin of the jump.
- if (registerIndex == Accumulator && instructionWritesAccumulatorWithoutReading)
- continue;
-
auto newType = registerIt.value();
if (!newType.isValid()) {
setError(u"When reached from offset %1, %2 is undefined"_qs
@@ -1578,60 +1977,34 @@ QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
auto currentRegister = m_state.registers.find(registerIndex);
if (currentRegister != m_state.registers.end()) {
- // Careful with accessing the hash iterator later inside this block,
- // some operations may invalidate it.
- auto currentRegisterType = currentRegister;
-
- if (*currentRegisterType != newType) {
- auto merged = m_typeResolver->merge(newType, *currentRegisterType);
- Q_ASSERT(merged.isValid());
- m_state.annotations[currentInstructionOffset()]
- .expectedTargetTypesBeforeJump[registerIndex] = merged;
- setRegister(registerIndex, merged);
+ if (currentRegister.value() != newType) {
+ mergeRegister(registerIndex, newType, currentRegister.value());
} else {
// Clear the constant value as this from a jump that might be merging two
// different value
// currentRegister->m_state.value = {};
}
+ } else {
+ mergeRegister(registerIndex, newType, newType);
}
}
}
- // Most instructions require a valid accumulator as input
- switch (instr) {
- case QV4::Moth::Instr::Type::Jump:
- case QV4::Moth::Instr::Type::LoadReg:
- case QV4::Moth::Instr::Type::LoadName:
- case QV4::Moth::Instr::Type::LoadRuntimeString:
- case QV4::Moth::Instr::Type::LoadInt:
- case QV4::Moth::Instr::Type::LoadNull:
- case QV4::Moth::Instr::Type::TypeofName:
- case QV4::Moth::Instr::Type::CallProperty:
- case QV4::Moth::Instr::Type::CallName:
- case QV4::Moth::Instr::Type::MoveReg:
- case QV4::Moth::Instr::Type::MoveConst:
- case QV4::Moth::Instr::Type::DefineArray:
- case QV4::Moth::Instr::Type::DefineObjectLiteral:
- case QV4::Moth::Instr::Type::CheckException:
- case QV4::Moth::Instr::Type::CreateCallContext:
- case QV4::Moth::Instr::Type::PopContext:
- case QV4::Moth::Instr::Type::JumpNoException:
- case QV4::Moth::Instr::Type::SetUnwindHandler:
- case QV4::Moth::Instr::Type::PushCatchContext:
- case QV4::Moth::Instr::Type::UnwindDispatch:
- break;
- default:
- if (instructionWritesAccumulatorWithoutReading)
- m_state.accumulatorIn = QQmlJSRegisterContent();
- else
- m_state.accumulatorIn = checkedInputRegister(Accumulator);
- }
-
return ProcessInstruction;
}
void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
{
+ InstructionAnnotation &currentInstruction = m_state.annotations[currentInstructionOffset()];
+ currentInstruction.changedRegister = m_state.changedRegister();
+ currentInstruction.changedRegisterIndex = m_state.changedRegisterIndex();
+ currentInstruction.readRegisters = m_state.takeReadRegisters();
+ currentInstruction.hasSideEffects = m_state.hasSideEffects();
+ currentInstruction.isRename = m_state.isRename();
+ m_state.setHasSideEffects(false);
+ m_state.setIsRename(false);
+ m_state.setReadRegisters(VirtualRegisters());
+
switch (instr) {
// the following instructions are not expected to produce output in the accumulator
case QV4::Moth::Instr::Type::Ret:
@@ -1653,30 +2026,45 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::SetUnwindHandler:
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
+ if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
+ setError(u"Instruction is not expected to populate the accumulator"_qs);
+ return;
+ }
break;
default:
// If the instruction is expected to produce output, save it in the register set
// for the next instruction.
- if (m_state.accumulatorOut.isValid()) {
- setRegister(Accumulator, m_state.accumulatorOut);
- m_state.accumulatorOut = QQmlJSRegisterContent();
- } else if (!m_error->isValid()) {
+ if ((!m_state.changedRegister().isValid() || m_state.changedRegisterIndex() != Accumulator)
+ && !m_error->isValid()) {
setError(u"Instruction is expected to populate the accumulator"_qs);
return;
}
}
- m_state.annotations[currentInstructionOffset()].registers = m_state.registers;
+ if (m_state.changedRegisterIndex() != InvalidRegister) {
+ Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
+ m_state.registers[m_state.changedRegisterIndex()] = m_state.changedRegister();
+ m_state.clearChangedRegister();
+ }
}
-void QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
+QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
{
auto lhsRegister = checkedInputRegister(lhs);
if (!lhsRegister.isValid())
- return;
+ return QQmlJSRegisterContent();
- m_state.accumulatorOut =
- m_typeResolver->typeForBinaryOperation(op, lhsRegister, m_state.accumulatorIn);
+ const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
+ op, lhsRegister, m_state.accumulatorIn());
+
+ setAccumulator(type);
+
+ // If we're dealing with QJSPrimitiveType, do not force premature conversion of the arguemnts
+ // to the target type. Such an operation can lose information.
+ if (type.storedType() == m_typeResolver->jsPrimitiveType())
+ return m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+
+ return type;
}
void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
@@ -1692,8 +2080,10 @@ void QQmlJSTypePropagator::saveRegisterStateForJump(int offset)
const auto registerStates =
m_jumpOriginRegisterStateByTargetInstructionOffset.equal_range(jumpToOffset);
for (auto it = registerStates.first; it != registerStates.second; ++it) {
- if (it->registers == state.registers)
+ if (it->registers.keys() == state.registers.keys()
+ && it->registers.values() == state.registers.values()) {
return; // We've seen the same register state before. No need for merging.
+ }
}
// The register state at the target offset needs to be resolved in a further pass.
@@ -1715,28 +2105,14 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const
registerIndex - FirstArgument - m_function->argumentTypes.count());
}
-// As the source register content may also be a register, we expect a copy here,
-// rather than a reference. Otherwise you might pass a reference to another entry
-// of m_state.registers, which then becomes invalid when making space for the new
-// entry in the hash.
-void QQmlJSTypePropagator::setRegister(int index, QQmlJSRegisterContent content)
-{
- m_state.registers[index] = std::move(content);
-}
-
-void QQmlJSTypePropagator::setRegister(int index, const QQmlJSScope::ConstPtr &content)
-{
- m_state.registers[index] = m_typeResolver->globalType(content);
-}
-
QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
{
- VirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
- if (regIt == m_state.registers.constEnd()) {
+ const auto regIt = m_state.registers.find(reg);
+ if (regIt == m_state.registers.end()) {
setError(u"Type error: could not infer the type of an expression"_qs);
return {};
}
- return *regIt;
+ return regIt.value();
}
bool QQmlJSTypePropagator::canConvertFromTo(const QQmlJSRegisterContent &from,