aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-11-15 17:48:36 +0100
committerUlf Hermann <ulf.hermann@qt.io>2021-11-17 18:04:41 +0100
commit0c553510e08d57975c91966c42a991ae43ab2e63 (patch)
tree9682f4f34555227325c373b5e6e28104126e1c96
parentd44dc4a25bf89931e6fd09433a9ad9ab4ae9dd7d (diff)
QmlCompiler: Introduce compile passes
Concentrate code likely to be used by more than one pass in a base class, and adapt the existing users. Also, stub out all the byte code visitors so that we can easily implement passes that only target part of the byte code. Task-number: QTBUG-98305 Change-Id: Ib1e16daf678bf478d9d2d11b3604ded3749f2096 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
-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