aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmlcompiler/CMakeLists.txt1
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h307
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h3
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp116
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h68
-rw-r--r--tools/qmllint/codegen.cpp99
-rw-r--r--tools/qmllint/codegen.h24
7 files changed, 414 insertions, 204 deletions
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 6428a3df26..03812d359b 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_module(QmlCompilerPrivate
qcoloroutput_p.h qcoloroutput.cpp
qdeferredpointer_p.h
qqmljsannotation_p.h qqmljsannotation.cpp
+ qqmljscompilepass_p.h
qqmljscompiler.cpp qqmljscompiler_p.h
qqmljsimporter.cpp qqmljsimporter_p.h
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
new file mode 100644
index 0000000000..a02d63c0d8
--- /dev/null
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLJSCOMPILEPASS_P_H
+#define QQMLJSCOMPILEPASS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+
+#include <private/qqmljscompiler_p.h>
+#include <private/qqmljslogger_p.h>
+#include <private/qqmljsregistercontent_p.h>
+#include <private/qqmljsscope_p.h>
+#include <private/qqmljstyperesolver_p.h>
+#include <private/qv4bytecodehandler_p.h>
+#include <private/qv4compiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
+{
+ Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
+public:
+ enum RegisterShortcuts {
+ Accumulator = QV4::CallData::Accumulator,
+ FirstArgument = QV4::CallData::OffsetCount
+ };
+
+ using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable;
+
+ // map from register index to expected type
+ using VirtualRegisters = QHash<int, QQmlJSRegisterContent>;
+
+ struct InstructionAnnotation
+ {
+ VirtualRegisters registers;
+ VirtualRegisters expectedTargetTypesBeforeJump;
+ };
+
+ using InstructionAnnotations = QHash<int, InstructionAnnotation>;
+
+ struct Function
+ {
+ QHash<QString, QQmlJSScope::ConstPtr> addressableScopes;
+ QList<QQmlJSScope::ConstPtr> argumentTypes;
+ QQmlJSScope::ConstPtr returnType;
+ QQmlJSScope::ConstPtr qmlScope;
+ QByteArray code;
+ const SourceLocationTable *sourceLocations = nullptr;
+ bool isSignalHandler = false;
+ bool isQPropertyBinding = false;
+ };
+
+ struct State
+ {
+ VirtualRegisters registers;
+ QQmlJSRegisterContent accumulatorIn;
+ QQmlJSRegisterContent accumulatorOut;
+ };
+
+ QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ : m_jsUnitGenerator(jsUnitGenerator)
+ , m_typeResolver(typeResolver)
+ , m_logger(logger)
+ {}
+
+protected:
+ const QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr;
+ const QQmlJSTypeResolver *m_typeResolver = nullptr;
+ QQmlJSLogger *m_logger = nullptr;
+
+ const Function *m_function = nullptr;
+ QQmlJS::DiagnosticMessage *m_error = nullptr;
+
+ State initialState(const Function *function, const QQmlJSTypeResolver *resolver)
+ {
+ State state;
+ for (int i = 0; i < function->argumentTypes.count(); ++i) {
+ state.registers[FirstArgument + i]
+ = resolver->globalType(function->argumentTypes.at(i));
+ }
+ return state;
+ }
+
+ State nextStateFromAnnotations(
+ const State &oldState, const InstructionAnnotations &annotations)
+ {
+ State newState;
+
+ // Usually the initial accumulator type is the output of the previous instruction, but ...
+ newState.accumulatorIn = oldState.accumulatorOut;
+
+ const auto instruction = annotations.constFind(currentInstructionOffset());
+ if (instruction != annotations.constEnd()) {
+ const auto target = instruction->expectedTargetTypesBeforeJump.constFind(Accumulator);
+ if (target != instruction->expectedTargetTypesBeforeJump.constEnd()) {
+ // ... the initial type of the accumulator is given in expectedTargetTypesBeforeJump
+ // if the current instruction can be jumped to.
+ newState.accumulatorIn = *target;
+ }
+
+ newState.registers = instruction->registers;
+ newState.accumulatorOut = instruction->registers[Accumulator];
+ } else {
+ newState.registers = VirtualRegisters();
+ newState.accumulatorOut = QQmlJSRegisterContent();
+ }
+
+ return newState;
+ }
+
+ QQmlJS::SourceLocation currentSourceLocation() const
+ {
+ Q_ASSERT(m_function);
+ Q_ASSERT(m_function->sourceLocations);
+ const int offset = currentInstructionOffset();
+ const auto &entries = m_function->sourceLocations->entries;
+ auto item = std::lower_bound(entries.begin(), entries.end(), offset,
+ [](auto entry, uint offset) { return entry.offset < offset; });
+
+ Q_ASSERT(item != entries.end());
+ return item->location;
+ }
+
+ void setError(const QString &message)
+ {
+ Q_ASSERT(m_error);
+ if (m_error->isValid())
+ return;
+ m_error->message = message;
+ m_error->loc = currentSourceLocation();
+ }
+
+ // Stub out all the methods so that passes can choose to only implement part of them.
+ void generate_Add(int) override {}
+ void generate_As(int) override {}
+ void generate_BitAnd(int) override {}
+ void generate_BitAndConst(int) override {}
+ void generate_BitOr(int) override {}
+ void generate_BitOrConst(int) override {}
+ void generate_BitXor(int) override {}
+ void generate_BitXorConst(int) override {}
+ void generate_CallElement(int, int, int, int) override {}
+ void generate_CallGlobalLookup(int, int, int) override {}
+ void generate_CallName(int, int, int) override {}
+ void generate_CallPossiblyDirectEval(int, int) override {}
+ void generate_CallProperty(int, int, int, int) override {}
+ void generate_CallPropertyLookup(int, int, int, int) override {}
+ void generate_CallQmlContextPropertyLookup(int, int, int) override {}
+ void generate_CallValue(int, int, int) override {}
+ void generate_CallWithReceiver(int, int, int, int) override {}
+ void generate_CallWithSpread(int, int, int, int) override {}
+ void generate_CheckException() override {}
+ void generate_CloneBlockContext() override {}
+ void generate_CmpEq(int) override {}
+ void generate_CmpEqInt(int) override {}
+ void generate_CmpEqNull() override {}
+ void generate_CmpGe(int) override {}
+ void generate_CmpGt(int) override {}
+ void generate_CmpIn(int) override {}
+ void generate_CmpInstanceOf(int) override {}
+ void generate_CmpLe(int) override {}
+ void generate_CmpLt(int) override {}
+ void generate_CmpNe(int) override {}
+ void generate_CmpNeInt(int) override {}
+ void generate_CmpNeNull() override {}
+ void generate_CmpStrictEqual(int) override {}
+ void generate_CmpStrictNotEqual(int) override {}
+ void generate_Construct(int, int, int) override {}
+ void generate_ConstructWithSpread(int, int, int) override {}
+ void generate_ConvertThisToObject() override {}
+ void generate_CreateCallContext() override {}
+ void generate_CreateClass(int, int, int) override {}
+ void generate_CreateMappedArgumentsObject() override {}
+ void generate_CreateRestParameter(int) override {}
+ void generate_CreateUnmappedArgumentsObject() override {}
+ void generate_DeadTemporalZoneCheck(int) override {}
+ void generate_Debug() override {}
+ void generate_DeclareVar(int, int) override {}
+ void generate_Decrement() override {}
+ void generate_DefineArray(int, int) override {}
+ void generate_DefineObjectLiteral(int, int, int) override {}
+ void generate_DeleteName(int) override {}
+ void generate_DeleteProperty(int, int) override {}
+ void generate_DestructureRestElement() override {}
+ void generate_Div(int) override {}
+ void generate_Exp(int) override {}
+ void generate_GetException() override {}
+ void generate_GetIterator(int) override {}
+ void generate_GetLookup(int) override {}
+ void generate_GetOptionalLookup(int, int) override {}
+ void generate_GetTemplateObject(int) override {}
+ void generate_Increment() override {}
+ void generate_InitializeBlockDeadTemporalZone(int, int) override {}
+ void generate_IteratorClose(int) override {}
+ void generate_IteratorNext(int, int) override {}
+ void generate_IteratorNextForYieldStar(int, int) override {}
+ void generate_Jump(int) override {}
+ void generate_JumpFalse(int) override {}
+ void generate_JumpNoException(int) override {}
+ void generate_JumpNotUndefined(int) override {}
+ void generate_JumpTrue(int) override {}
+ void generate_LoadClosure(int) override {}
+ void generate_LoadConst(int) override {}
+ void generate_LoadElement(int) override {}
+ void generate_LoadFalse() override {}
+ void generate_LoadGlobalLookup(int) override {}
+ void generate_LoadImport(int) override {}
+ void generate_LoadInt(int) override {}
+ void generate_LoadLocal(int) override {}
+ void generate_LoadName(int) override {}
+ void generate_LoadNull() override {}
+ void generate_LoadOptionalProperty(int, int) override {}
+ void generate_LoadProperty(int) override {}
+ void generate_LoadQmlContextPropertyLookup(int) override {}
+ void generate_LoadReg(int) override {}
+ void generate_LoadRuntimeString(int) override {}
+ void generate_LoadScopedLocal(int, int) override {}
+ void generate_LoadSuperConstructor() override {}
+ void generate_LoadSuperProperty(int) override {}
+ void generate_LoadTrue() override {}
+ void generate_LoadUndefined() override {}
+ void generate_LoadZero() override {}
+ void generate_Mod(int) override {}
+ void generate_MoveConst(int, int) override {}
+ void generate_MoveReg(int, int) override {}
+ void generate_MoveRegExp(int, int) override {}
+ void generate_Mul(int) override {}
+ void generate_PopContext() override {}
+ void generate_PopScriptContext() override {}
+ void generate_PushBlockContext(int) override {}
+ void generate_PushCatchContext(int, int) override {}
+ void generate_PushScriptContext(int) override {}
+ void generate_PushWithContext() override {}
+ void generate_Resume(int) override {}
+ void generate_Ret() override {}
+ void generate_SetException() override {}
+ void generate_SetLookup(int, int) override {}
+ void generate_SetUnwindHandler(int) override {}
+ void generate_Shl(int) override {}
+ void generate_ShlConst(int) override {}
+ void generate_Shr(int) override {}
+ void generate_ShrConst(int) override {}
+ void generate_StoreElement(int, int) override {}
+ void generate_StoreLocal(int) override {}
+ void generate_StoreNameSloppy(int) override {}
+ void generate_StoreNameStrict(int) override {}
+ void generate_StoreProperty(int, int) override {}
+ void generate_StoreReg(int) override {}
+ void generate_StoreScopedLocal(int, int) override {}
+ void generate_StoreSuperProperty(int) override {}
+ void generate_Sub(int) override {}
+ void generate_TailCall(int, int, int, int) override {}
+ void generate_ThrowException() override {}
+ void generate_ThrowOnNullOrUndefined() override {}
+ void generate_ToObject() override {}
+ void generate_TypeofName(int) override {}
+ void generate_TypeofValue() override {}
+ void generate_UCompl() override {}
+ void generate_UMinus() override {}
+ void generate_UNot() override {}
+ void generate_UPlus() override {}
+ void generate_UShr(int) override {}
+ void generate_UShrConst(int) override {}
+ void generate_UnwindDispatch() override {}
+ void generate_UnwindToLabel(int, int) override {}
+ void generate_Yield() override {}
+ void generate_YieldStar() override {}
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSCOMPILEPASS_P_H
diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h
index 2e3f9eb016..dfad7a6cf1 100644
--- a/src/qmlcompiler/qqmljsregistercontent_p.h
+++ b/src/qmlcompiler/qqmljsregistercontent_p.h
@@ -191,9 +191,6 @@ private:
// TODO: Constant string/number/bool/enumval
};
-// map from register index to expected type
-using QQmlJSVirtualRegisters = QHash<int, QQmlJSRegisterContent>;
-
QT_END_NAMESPACE
#endif // REGISTERCONTENT_H
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 258e89f26e..198907d3dc 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -34,50 +34,28 @@ QT_BEGIN_NAMESPACE
QQmlJSTypePropagator::QQmlJSTypePropagator(QV4::Compiler::JSUnitGenerator *unitGenerator,
QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
QQmlJSTypeInfo *typeInfo)
- : m_typeResolver(typeResolver),
- m_jsUnitGenerator(unitGenerator),
- m_logger(logger),
- m_typeInfo(typeInfo)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger), m_typeInfo(typeInfo)
{
}
-QQmlJSTypePropagator::~QQmlJSTypePropagator() { }
-
-QQmlJSTypePropagator::TypePropagationResult QQmlJSTypePropagator::propagateTypes(
- const QV4::Compiler::Context *context, const QQmlJS::AST::BoundNames &arguments,
- const QQmlJSScope::ConstPtr &returnType, const QQmlJSScope::ConstPtr &qmlScope,
- const QHash<QString, QQmlJSScope::ConstPtr> &addressableScopes, bool isSignalHandler,
- Error *error)
+QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
+ const Function *function, QQmlJS::DiagnosticMessage *error)
{
+ m_function = function;
m_error = error;
- m_currentScope = qmlScope;
- m_currentContext = context;
- m_addressableScopes = addressableScopes;
- m_arguments = arguments;
- m_returnType = m_typeResolver->globalType(returnType);
- m_isSignalHandler = isSignalHandler;
+ m_returnType = m_typeResolver->globalType(m_function->returnType);
do {
m_state = PassState();
-
- for (int i = 0; i < arguments.count(); ++i) {
- auto argument = arguments.at(i);
- setRegister(FirstArgument + i,
- argument.typeAnnotation
- ? m_typeResolver->typeFromAST(argument.typeAnnotation->type)
- : m_typeResolver->varType());
- }
-
- const int registerCount = context->registerCountInFunction;
- for (int i = FirstArgument + arguments.count(); i < registerCount; ++i)
- setRegister(i, m_typeResolver->voidType());
+ m_state.State::operator=(initialState(m_function, m_typeResolver));
reset();
- decode(context->code.constData(), static_cast<uint>(context->code.length()));
+ decode(m_function->code.constData(), static_cast<uint>(m_function->code.length()));
- if (m_error->isSet())
- break;
- } while (m_state.needsMorePasses);
+ // 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);
return m_state.annotations;
}
@@ -95,7 +73,7 @@ QQmlJSTypePropagator::TypePropagationResult QQmlJSTypePropagator::propagateTypes
void QQmlJSTypePropagator::generate_Ret()
{
- if (m_isSignalHandler) {
+ if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
} else if (!m_returnType.isValid() && m_state.accumulatorIn.isValid()
&& m_typeResolver->containedType(m_state.accumulatorIn)
@@ -236,7 +214,7 @@ 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_currentScope, name);
+ m_state.accumulatorOut = m_typeResolver->scopedType(m_function->qmlScope, name);
if (!m_state.accumulatorOut.isValid())
setError(u"Cannot find name "_qs + name);
}
@@ -248,8 +226,8 @@ void QQmlJSTypePropagator::generate_LoadGlobalLookup(int index)
QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentSourceLocation() const
{
- Q_ASSERT(m_currentContext->sourceLocationTable);
- const auto &entries = m_currentContext->sourceLocationTable->entries;
+ Q_ASSERT(m_function->sourceLocations);
+ const auto &entries = m_function->sourceLocations->entries;
auto item = std::lower_bound(entries.begin(), entries.end(), currentInstructionOffset(),
[](auto entry, uint offset) { return entry.offset < offset; });
@@ -261,8 +239,8 @@ QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentSourceLocation() const
QQmlJS::SourceLocation QQmlJSTypePropagator::getCurrentBindingSourceLocation() const
{
- Q_ASSERT(m_currentContext->sourceLocationTable);
- const auto &entries = m_currentContext->sourceLocationTable->entries;
+ Q_ASSERT(m_function->sourceLocations);
+ const auto &entries = m_function->sourceLocations->entries;
Q_ASSERT(!entries.isEmpty());
return combine(entries.constFirst().location, entries.constLast().location);
@@ -272,17 +250,17 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
{
auto location = getCurrentSourceLocation();
- if (m_currentScope->isInCustomParserParent()) {
- Q_ASSERT(!m_currentScope->baseType().isNull());
+ if (m_function->qmlScope->isInCustomParserParent()) {
+ Q_ASSERT(!m_function->qmlScope->baseType().isNull());
// Only ignore custom parser based elements if it's not Connections.
- if (m_currentScope->baseType()->internalName() != u"QQmlConnections"_qs)
+ if (m_function->qmlScope->baseType()->internalName() != u"QQmlConnections"_qs)
return;
}
m_logger->logWarning(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location);
- auto childScopes = m_currentScope->childScopes();
- for (qsizetype i = 0; i < m_currentScope->childScopes().length(); i++) {
+ auto childScopes = m_function->qmlScope->childScopes();
+ for (qsizetype i = 0; i < m_function->qmlScope->childScopes().length(); i++) {
auto &scope = childScopes[i];
if (location.offset > scope->sourceLocation().offset) {
if (i + 1 < childScopes.length()
@@ -331,10 +309,11 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
}
}
- for (QQmlJSScope::ConstPtr scope = m_currentScope; !scope.isNull();
+ for (QQmlJSScope::ConstPtr scope = m_function->qmlScope; !scope.isNull();
scope = scope->parentScope()) {
if (scope->hasProperty(name)) {
- auto it = std::find_if(m_addressableScopes.constBegin(), m_addressableScopes.constEnd(),
+ auto it = std::find_if(m_function->addressableScopes.constBegin(),
+ m_function->addressableScopes.constEnd(),
[&](const QQmlJSScope::ConstPtr ptr) { return ptr == scope; });
FixSuggestion suggestion { Log_UnqualifiedAccess, QtInfoMsg, {} };
@@ -342,7 +321,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
QQmlJS::SourceLocation fixLocation = location;
fixLocation.length = 0;
- QString id = it == m_addressableScopes.constEnd() ? u"<id>"_qs : it.key();
+ QString id = it == m_function->addressableScopes.constEnd() ? u"<id>"_qs : it.key();
suggestion.fixes << FixSuggestion::Fix {
name + QLatin1String(" is a member of a parent element\n")
@@ -351,7 +330,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name) const
QtInfoMsg, fixLocation, id + u"."_qs
};
- if (it == m_addressableScopes.constEnd()) {
+ if (it == m_function->addressableScopes.constEnd()) {
suggestion.fixes << FixSuggestion::Fix {
u"You first have to give the element an id"_qs,
QtInfoMsg,
@@ -444,7 +423,7 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
m_state.accumulatorOut = m_typeResolver->scopedType(
- m_currentScope,
+ m_function->qmlScope,
m_state.accumulatorIn.isImportNamespace()
? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn.importNamespace())
+ u'.' + name
@@ -454,18 +433,18 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
&& m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ScopeAttached) {
QQmlJSScope::ConstPtr attachedType = m_state.accumulatorOut.scopeType();
- for (QQmlJSScope::ConstPtr scope = m_currentScope->parentScope(); !scope.isNull();
+ for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
scope = scope->parentScope()) {
if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
- auto it = std::find(m_addressableScopes.constBegin(),
- m_addressableScopes.constEnd(), scope);
+ auto it = std::find(m_function->addressableScopes.constBegin(),
+ m_function->addressableScopes.constEnd(), scope);
FixSuggestion suggestion { Log_AttachedPropertyReuse, QtInfoMsg, {} };
QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
fixLocation.length = 0;
- QString id = it == m_addressableScopes.constEnd() ? u"<id>"_qs : it.key();
+ QString id = it == m_function->addressableScopes.constEnd() ? u"<id>"_qs : it.key();
suggestion.fixes << FixSuggestion::Fix {
u"Using attached type %1 already initialized in a parent scope. Reference it by id instead:"_qs
@@ -476,7 +455,7 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
fixLocation = scope->sourceLocation();
fixLocation.length = 0;
- if (it == m_addressableScopes.constEnd()) {
+ if (it == m_function->addressableScopes.constEnd()) {
suggestion.fixes
<< FixSuggestion::Fix { u"You first have to give the element an id"_qs,
QtInfoMsg,
@@ -488,20 +467,20 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
}
}
- m_typeInfo->usedAttachedTypes.insert(m_currentScope, attachedType);
+ m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
}
if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
const QQmlJSRegisterContent inType = m_state.accumulatorIn.isValid()
? m_state.accumulatorIn
- : m_typeResolver->globalType(m_currentScope);
+ : m_typeResolver->globalType(m_function->qmlScope);
m_state.accumulatorOut = QQmlJSRegisterContent::create(
inType.storedType(), nameIndex, QQmlJSRegisterContent::ScopeModulePrefix,
m_typeResolver->containedType(inType));
return;
}
- checkDeprecated(m_currentScope, name, false);
+ checkDeprecated(m_function->qmlScope, name, false);
bool isRestricted = checkRestricted(name);
@@ -520,7 +499,7 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_currentScope, name);
+ const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
if (!type.isValid()) {
setError(u"Cannot find name "_qs + name);
@@ -532,7 +511,7 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
return;
}
- if (!type.isWritable() && !m_currentScope->hasOwnProperty(name)) {
+ 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,
@@ -901,7 +880,7 @@ void QQmlJSTypePropagator::generate_CallPossiblyDirectEval(int argc, int argv)
void QQmlJSTypePropagator::propagateScopeLookupCall(const QString &functionName, int argc, int argv)
{
- if (QQmlJSScope::ConstPtr scope = QQmlJSScope::findCurrentQMLScope(m_currentScope)) {
+ if (QQmlJSScope::ConstPtr scope = QQmlJSScope::findCurrentQMLScope(m_function->qmlScope)) {
const auto methods = scope->methods(functionName);
if (!methods.isEmpty()) {
propagateCall(methods, argc, argv);
@@ -921,7 +900,7 @@ void QQmlJSTypePropagator::generate_CallQmlContextPropertyLookup(int index, int
{
const QString name = m_jsUnitGenerator->lookupName(index);
propagateScopeLookupCall(name, argc, argv);
- checkDeprecated(m_currentScope, name, true);
+ checkDeprecated(m_function->qmlScope, name, true);
}
void QQmlJSTypePropagator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
@@ -1481,7 +1460,7 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
QV4::Moth::ByteCodeHandler::Verdict
QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type instr)
{
- if (m_error->isSet())
+ if (m_error->isValid())
return SkipInstruction;
if (m_state.jumpTargets.contains(currentInstructionOffset()))
@@ -1632,7 +1611,7 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
if (m_state.accumulatorOut.isValid()) {
setRegister(Accumulator, m_state.accumulatorOut);
m_state.accumulatorOut.reset();
- } else if (!m_error->isSet()) {
+ } else if (!m_error->isValid()) {
setError(u"Instruction is expected to populate the accumulator"_qs);
return;
}
@@ -1678,12 +1657,13 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const
{
if (registerIndex == Accumulator)
return u"accumulator"_qs;
- if (registerIndex >= FirstArgument && registerIndex < FirstArgument + m_arguments.count()) {
- const int argIdx = registerIndex - FirstArgument;
- return u"argument %1 (\"%2\")"_qs.arg(argIdx).arg(m_arguments.at(argIdx).id);
+ if (registerIndex >= FirstArgument
+ && registerIndex < FirstArgument + m_function->argumentTypes.count()) {
+ return u"argument %1"_qs.arg(registerIndex - FirstArgument);
}
- return u"temporary register %1"_qs.arg(registerIndex - FirstArgument - m_arguments.count());
+ return u"temporary register %1"_qs.arg(
+ registerIndex - FirstArgument - m_function->argumentTypes.count());
}
void QQmlJSTypePropagator::setRegister(int index, const QQmlJSRegisterContent &content)
@@ -1698,7 +1678,7 @@ void QQmlJSTypePropagator::setRegister(int index, const QQmlJSScope::ConstPtr &c
QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg)
{
- QQmlJSVirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
+ VirtualRegisters::ConstIterator regIt = m_state.registers.find(reg);
if (regIt == m_state.registers.constEnd()) {
setError(u"Type error: could not infer the type of an expression"_qs);
return {};
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index e40e85054d..718454cfd1 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -39,45 +39,20 @@
//
// We mean it.
-#include <private/qv4bytecodehandler_p.h>
-#include <private/qv4compiler_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsscope_p.h>
-#include <private/qqmljslogger_p.h>
+#include <private/qqmljscompilepass_p.h>
-#include "qqmljstyperesolver_p.h"
-#include "qqmljsregistercontent_p.h"
QT_BEGIN_NAMESPACE
-struct QQmlJSTypePropagator : public QV4::Moth::ByteCodeHandler
+struct QQmlJSTypePropagator : public QQmlJSCompilePass
{
QQmlJSTypePropagator(QV4::Compiler::JSUnitGenerator *unitGenerator,
QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
QQmlJSTypeInfo *typeInfo = nullptr);
- ~QQmlJSTypePropagator();
- auto returnType() const { return m_returnType; }
- struct Error
- {
- QString message;
- int instructionOffset;
- bool isSet() const { return !message.isEmpty(); }
- };
-
- struct InstructionAnnotation
- {
- QQmlJSVirtualRegisters registers;
- QQmlJSVirtualRegisters expectedTargetTypesBeforeJump;
- };
-
- using TypePropagationResult = QHash<int, InstructionAnnotation>;
-
- TypePropagationResult
- propagateTypes(const QV4::Compiler::Context *context, const QQmlJS::AST::BoundNames &arguments,
- const QQmlJSScope::ConstPtr &returnType, const QQmlJSScope::ConstPtr &qmlScope,
- const QHash<QString, QQmlJSScope::ConstPtr> &addressableScopes,
- bool isSignalHandler, Error *error);
+ InstructionAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error);
void generate_Ret() override;
void generate_Debug() override;
@@ -218,34 +193,18 @@ struct QQmlJSTypePropagator : public QV4::Moth::ByteCodeHandler
Verdict startInstruction(QV4::Moth::Instr::Type instr) override;
void endInstruction(QV4::Moth::Instr::Type instr) override;
- enum RegisterShortcuts {
- Accumulator = QV4::CallData::Accumulator,
- FirstArgument = QV4::CallData::OffsetCount
- };
-
private:
- void setError(const QString &message)
- {
- m_error->message = message;
- m_error->instructionOffset = currentInstructionOffset();
- }
-
struct ExpectedRegisterState
{
int originatingOffset = 0;
- QQmlJSVirtualRegisters registers;
+ VirtualRegisters registers;
};
- struct PassState
+ struct PassState : QQmlJSCompilePass::State
{
- bool skipInstructionsUntilNextJumpTarget = false;
+ InstructionAnnotations annotations;
QSet<int> jumpTargets;
-
- QQmlJSVirtualRegisters registers;
- QQmlJSRegisterContent accumulatorIn;
- QQmlJSRegisterContent accumulatorOut;
-
- QHash<int, InstructionAnnotation> annotations;
+ bool skipInstructionsUntilNextJumpTarget = false;
bool needsMorePasses = false;
};
@@ -273,24 +232,13 @@ private:
QQmlJSMetaMethod bestMatchForCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv,
QStringList *errors);
- QQmlJS::AST::BoundNames m_arguments;
QQmlJSRegisterContent m_returnType;
- bool m_isSignalHandler = false;
-
- QQmlJSTypeResolver *m_typeResolver = nullptr;
- QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr;
-
- QQmlJSScope::ConstPtr m_currentScope;
- const QV4::Compiler::Context *m_currentContext;
- QHash<QString, QQmlJSScope::ConstPtr> m_addressableScopes;
- QQmlJSLogger *m_logger;
- QQmlJSTypeInfo *m_typeInfo;
+ 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;
PassState m_state;
- Error *m_error = nullptr;
};
QT_END_NAMESPACE
diff --git a/tools/qmllint/codegen.cpp b/tools/qmllint/codegen.cpp
index 4b9137316b..849953f613 100644
--- a/tools/qmllint/codegen.cpp
+++ b/tools/qmllint/codegen.cpp
@@ -125,7 +125,6 @@ Codegen::compileBinding(const QV4::Compiler::Context *context, const QmlIR::Bind
}
Function function;
- function.contextType = QV4::Compiler::ContextType::Binding;
function.qmlScope = m_scopeType;
const QString propertyName = m_document->stringAt(irBinding.propertyNameIndex);
@@ -183,8 +182,8 @@ Codegen::compileBinding(const QV4::Compiler::Context *context, const QmlIR::Bind
auto astNode =
m_currentObject->functionsAndExpressions->slowAt(irBinding.value.compiledScriptIndex)
->node;
- function.ast = astNode->asFunctionDefinition();
- if (!function.ast) {
+ auto ast = astNode->asFunctionDefinition();
+ if (!ast) {
QQmlJS::AST::Statement *stmt = astNode->statementCast();
if (!stmt) {
Q_ASSERT(astNode->expressionCast());
@@ -195,20 +194,21 @@ Codegen::compileBinding(const QV4::Compiler::Context *context, const QmlIR::Bind
body = body->finish();
QString name = "binding for "; // ####
- function.ast = new (m_pool) QQmlJS::AST::FunctionDeclaration(m_pool->newString(name),
+ ast = new (m_pool) QQmlJS::AST::FunctionDeclaration(m_pool->newString(name),
/*formals*/ nullptr, body);
- function.ast->lbraceToken = astNode->firstSourceLocation();
- function.ast->functionToken = function.ast->lbraceToken;
- function.ast->rbraceToken = astNode->lastSourceLocation();
+ ast->lbraceToken = astNode->firstSourceLocation();
+ ast->functionToken = ast->lbraceToken;
+ ast->rbraceToken = astNode->lastSourceLocation();
}
- if (!generateFunction(context, &function)) {
+ QQmlJS::DiagnosticMessage error;
+ if (!generateFunction(QV4::Compiler::ContextType::Binding, context, ast, &function, &error)) {
// If it's a signal and the function just returns a closure, it's harmless.
// Otherwise promote the message to warning level.
- return diagnose(QStringLiteral("Could not compile binding for %1: %2")
- .arg(propertyName, function.error.message),
- (isSignal && function.error.type == QtDebugMsg) ? QtDebugMsg : QtWarningMsg,
- function.error.loc);
+ return diagnose(
+ QStringLiteral("Could not compile binding for %1: %2")
+ .arg(propertyName, error.message),
+ (isSignal && error.type == QtDebugMsg) ? QtDebugMsg : QtWarningMsg, error.loc);
}
return QQmlJSAotFunction {};
@@ -224,17 +224,16 @@ Codegen::compileFunction(const QV4::Compiler::Context *context, const QmlIR::Fun
const QString functionName = m_document->stringAt(irFunction.nameIndex);
Function function;
- function.contextType = QV4::Compiler::ContextType::Function;
function.qmlScope = m_scopeType;
auto astNode = m_currentObject->functionsAndExpressions->slowAt(irFunction.index)->node;
- function.ast = astNode->asFunctionDefinition();
- Q_ASSERT(function.ast);
- if (!generateFunction(context, &function)) {
+ QQmlJS::DiagnosticMessage error;
+ if (!generateFunction(QV4::Compiler::ContextType::Function, context,
+ astNode->asFunctionDefinition(), &function, &error)) {
return diagnose(QStringLiteral("Could not compile function %1: %2")
- .arg(functionName, function.error.message),
- QtWarningMsg, function.error.loc);
+ .arg(functionName, error.message),
+ QtWarningMsg, error.loc);
}
return QQmlJSAotFunction {};
@@ -266,31 +265,22 @@ QQmlJS::DiagnosticMessage Codegen::diagnose(const QString &message, QtMsgType ty
return QQmlJS::DiagnosticMessage { message, type, location };
}
-void Codegen::instructionOffsetToSrcLocation(const QV4::Compiler::Context *context, uint offset,
- QQmlJS::SourceLocation *srcLoc) const
-{
- Q_ASSERT(context->sourceLocationTable);
- const auto &entries = context->sourceLocationTable->entries;
- auto item = std::lower_bound(entries.begin(), entries.end(), offset,
- [](auto entry, uint offset) { return entry.offset < offset; });
-
- Q_ASSERT(item != entries.end());
- *srcLoc = item->location;
-}
-
-bool Codegen::generateFunction(const QV4::Compiler::Context *context, Function *function) const
+bool Codegen::generateFunction(
+ QV4::Compiler::ContextType contextType,
+ const QV4::Compiler::Context *context,
+ QQmlJS::AST::FunctionExpression *ast,
+ Function *function,
+ QQmlJS::DiagnosticMessage *error) const
{
- const auto error = [&](const QString &message) {
- QQmlJS::DiagnosticMessage msg;
- msg.loc = function->ast->firstSourceLocation();
- msg.message = message;
- function->error = msg;
+ const auto fail = [&](const QString &message) {
+ error->loc = ast->firstSourceLocation();
+ error->message = message;
return false;
};
QQmlJS::AST::BoundNames arguments;
- if (function->ast->formals)
- arguments = function->ast->formals->formals();
+ if (ast->formals)
+ arguments = ast->formals->formals();
if (function->argumentTypes.isEmpty()) {
for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) {
@@ -301,11 +291,11 @@ bool Codegen::generateFunction(const QV4::Compiler::Context *context, Function *
function->argumentTypes.append(rawType);
continue;
} else {
- return error(QStringLiteral("Cannot store the argument type %1.")
+ return fail(QStringLiteral("Cannot store the argument type %1.")
.arg(rawType ? rawType->internalName() : "<unknown>"));
}
} else {
- return error(
+ return fail(
QStringLiteral("Functions without type annotations won't be compiled"));
return false;
}
@@ -315,33 +305,30 @@ bool Codegen::generateFunction(const QV4::Compiler::Context *context, Function *
QQmlJSTypePropagator propagator(m_unitGenerator, m_typeResolver.get(), m_logger, m_typeInfo);
if (!function->returnType) {
- if (function->ast->typeAnnotation) {
- function->returnType = m_typeResolver->typeFromAST(function->ast->typeAnnotation->type);
+ if (ast->typeAnnotation) {
+ function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
if (!function->returnType)
- return error(QStringLiteral("Cannot resolve return type"));
+ return fail(QStringLiteral("Cannot resolve return type"));
}
}
if (function->returnType) {
if (!m_typeResolver->storedType(
function->returnType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- return error(QStringLiteral("Cannot store the return type %1.")
+ return fail(QStringLiteral("Cannot store the return type %1.")
.arg(function->returnType->internalName()));
}
}
- QQmlJSTypePropagator::Error propagationError;
- auto typePropagationResult = propagator.propagateTypes(
- context, arguments, function->returnType, function->qmlScope,
- m_typeResolver->objectsById(),
- !function->returnType && function->contextType == QV4::Compiler::ContextType::Binding,
- &propagationError);
- if (propagationError.isSet()) {
- QQmlJS::DiagnosticMessage msg;
- msg.type = context->returnsClosure ? QtDebugMsg : QtWarningMsg;
- instructionOffsetToSrcLocation(context, propagationError.instructionOffset, &msg.loc);
- msg.message = propagationError.message;
- function->error = msg;
+ function->isSignalHandler = !function->returnType
+ && contextType == QV4::Compiler::ContextType::Binding;
+ function->addressableScopes = m_typeResolver->objectsById();
+ function->code = context->code;
+ function->sourceLocations = context->sourceLocationTable.get();
+
+ propagator.run(function, error);
+ if (error->isValid()) {
+ error->type = context->returnsClosure ? QtDebugMsg : QtWarningMsg;
return false;
}
diff --git a/tools/qmllint/codegen.h b/tools/qmllint/codegen.h
index 9cea0b16d7..1a9dff3ed8 100644
--- a/tools/qmllint/codegen.h
+++ b/tools/qmllint/codegen.h
@@ -52,6 +52,7 @@
#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
#include <QtQmlCompiler/private/qqmljslogger_p.h>
+#include <QtQmlCompiler/private/qqmljscompilepass_p.h>
class Codegen : public QQmlJSAotCompiler
{
@@ -70,20 +71,7 @@ public:
QQmlJSAotFunction globalCode() const override;
private:
- struct Function
- {
- QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Binding;
- QQmlJS::AST::FunctionExpression *ast = nullptr;
- bool isQPropertyBinding = false;
-
- QQmlJSScope::ConstPtr returnType;
- QList<QQmlJSScope::ConstPtr> argumentTypes;
- QQmlJSScope::ConstPtr qmlScope;
-
- QString generatedCode;
- QStringList includes;
- QQmlJS::DiagnosticMessage error;
- };
+ using Function = QQmlJSCompilePass::Function;
const QmlIR::Document *m_document = nullptr;
const QString m_fileName;
@@ -104,9 +92,11 @@ private:
QQmlJS::DiagnosticMessage diagnose(const QString &message, QtMsgType type,
const QQmlJS::SourceLocation &location);
- bool generateFunction(const QV4::Compiler::Context *context, Function *function) const;
- void instructionOffsetToSrcLocation(const QV4::Compiler::Context *context, uint offset,
- QQmlJS::SourceLocation *srcLoc) const;
+ bool generateFunction(QV4::Compiler::ContextType contextType,
+ const QV4::Compiler::Context *context,
+ QQmlJS::AST::FunctionExpression *ast,
+ Function *function,
+ QQmlJS::DiagnosticMessage *error) const;
};
#endif