diff options
| author | Dmitrii Akshintsev <[email protected]> | 2025-10-28 15:34:45 +0100 |
|---|---|---|
| committer | Dmitrii Akshintsev <[email protected]> | 2025-12-04 21:01:19 +0100 |
| commit | d99c610674ada29c5280c07b45ebfab7bc4e3b11 (patch) | |
| tree | 398bdeb4776c53702e7d84e449536cbefcbfb675 /src | |
| parent | 87cac29f88b96241bfa5e3e0f65a57a0e9002450 (diff) | |
Qml IR: add support for virtual and override keywords
This patch adds two new fields, IsVirtual and IsOverride, to CompiledData::Property.
These fields will later be used to populate QQmlPropertyData and to handle
property override semantics.
At a high level, this change focuses on the data flow from the AST to the IR,
laying the groundwork for future semantic resolution.
Also moves test helper Syntax namespace to the quicktestutils
Task-number: QTBUG-98320
Change-Id: Ic2a2e28df08d53c8752c49304bd5f7ff46916d08
Reviewed-by: Fabian Kosmale <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/common/qv4compileddata_p.h | 52 | ||||
| -rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 2 | ||||
| -rw-r--r-- | src/qml/compiler/qqmlirbuilder_p.h | 3 | ||||
| -rw-r--r-- | src/quicktestutils/qml/qmlutils.cpp | 35 | ||||
| -rw-r--r-- | src/quicktestutils/qml/qmlutils_p.h | 54 |
5 files changed, 138 insertions, 8 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 01121bd008..e00e3aedd0 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x48 // Hotfix 6.10 - meta object change +#define QV4_DATA_STRUCTURE_VERSION 0x49 // Added isVirtual and isOverride fields to property class QIODevice; class QQmlTypeNameCache; @@ -788,19 +788,43 @@ struct Signal }; static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +/* + * We aim to minimize the size of a struct as much as possible, while preserving at least 28 bits + * for each index. + * + * Note that Name and Type indices are provided by StringTableGenerator, containing a hashmap + * (and a list) of unique strings within one Compilation Unit. It sounds rather unrealistic to have + * 2^28 (260+ million) unique strings within 1 CU (even if it's some form of global registry), not + * to mention the amount of memory needed to maintain such a StringTableGenerator. + * Therefore, after some preliminary analysis, it seems that even 20 bits + * should be a rather conservative cap. + * + * However it doesn't seem to easily provide many benefits atm other than better (logically) grouped + * unions like, let's say nameIndexAndAttributes. + * + * Atm 32-bit member nameIndexAndVirtSpecifiers looks smth like this: + * + * NameIndexField IsVirtualField IsOverrideField IsFinalField + * |10100000101000111000001110000| 0 | 1 | 0 | + * + */ struct Property { private: - using NameIndexField = quint32_le_bitfield_member<0, 31>; - using FinalField = quint32_le_bitfield_member<31, 1>; + using NameIndexField = quint32_le_bitfield_member<0, 29>; + using IsVirtualField = quint32_le_bitfield_member<29, 1>; + using IsOverrideField = quint32_le_bitfield_member<30, 1>; + using IsFinalField = quint32_le_bitfield_member<31, 1>; using CommonTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>; using IsRequiredField = quint32_le_bitfield_member<28, 1>; using IsCommonTypeField = quint32_le_bitfield_member<29, 1>; using IsListField = quint32_le_bitfield_member<30, 1>; using IsReadOnlyField = quint32_le_bitfield_member<31, 1>; + public: - quint32_le_bitfield_union<NameIndexField, FinalField> nameIndexAndFinal; + quint32_le_bitfield_union<NameIndexField, IsVirtualField, IsOverrideField, IsFinalField> + nameIndexAndVirtSpecifiers; quint32_le_bitfield_union< CommonTypeOrTypeNameIndexField, IsRequiredField, @@ -809,11 +833,23 @@ public: IsReadOnlyField> data; Location location; - quint32 nameIndex() const { return nameIndexAndFinal.get<NameIndexField>(); } - void setNameIndex(int nameIndex) { nameIndexAndFinal.set<NameIndexField>(nameIndex); } + quint32 nameIndex() const { return nameIndexAndVirtSpecifiers.get<NameIndexField>(); } + void setNameIndex(int nameIndex) { nameIndexAndVirtSpecifiers.set<NameIndexField>(nameIndex); } + + bool isVirtual() const { return nameIndexAndVirtSpecifiers.get<IsVirtualField>(); } + void setIsVirtual(bool isVirtual) + { + nameIndexAndVirtSpecifiers.set<IsVirtualField>(isVirtual ? 1 : 0); + } + + bool isOverride() const { return nameIndexAndVirtSpecifiers.get<IsOverrideField>(); } + void setIsOverride(bool isOverride) + { + nameIndexAndVirtSpecifiers.set<IsOverrideField>(isOverride ? 1 : 0); + } - bool isFinal() const { return nameIndexAndFinal.get<FinalField>(); } - void setIsFinal(bool final) { nameIndexAndFinal.set<FinalField>(final ? 1 : 0); } + bool isFinal() const { return nameIndexAndVirtSpecifiers.get<IsFinalField>(); } + void setIsFinal(bool isFinal) { nameIndexAndVirtSpecifiers.set<IsFinalField>(isFinal ? 1 : 0); } void setCommonType(CommonType t) { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index bf124a2272..1605ab5f59 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1045,6 +1045,8 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) Property *property = New<Property>(); property->setIsReadOnly(node->isReadonly()); property->setIsRequired(node->isRequired()); + property->setIsVirtual(node->isVirtual()); + property->setIsOverride(node->isOverride()); property->setIsFinal(node->isFinal()); const QV4::CompiledData::CommonType builtinPropertyType diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 68b2b462f6..441830e33b 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -464,6 +464,9 @@ struct Q_QML_COMPILER_EXPORT Pragma struct Q_QML_COMPILER_EXPORT Document { + // disable it explicitly, it's implicitly deleted because of the Engine::_pool + Q_DISABLE_COPY_MOVE(Document) + Document(const QString &fileName, const QString &finalUrl, bool debugMode); QString code; QQmlJS::Engine jsParserEngine; diff --git a/src/quicktestutils/qml/qmlutils.cpp b/src/quicktestutils/qml/qmlutils.cpp index 5f573e790b..528a843f17 100644 --- a/src/quicktestutils/qml/qmlutils.cpp +++ b/src/quicktestutils/qml/qmlutils.cpp @@ -137,6 +137,41 @@ void gc(QQmlEngine &engine, GCFlags flags) gc(*engine.handle(), flags); } +namespace Syntax { + +auto stringView(const Word &word) -> QLatin1StringView +{ + return std::holds_alternative<Token>(word) ? spellFor(std::get<Token>(word)) + : std::get<QLatin1StringView>(word); +} + +auto toString(const Phrase &phrase) -> QString +{ + QString result; + for (const auto &word : phrase) { + result += stringView(word) + QLatin1Char(' '); + } + return result; +} + +// comfort +auto operator+(const Word &word, const Phrase &phrase) -> Phrase +{ + return Phrase{ word } + phrase; +}; + +auto operator+(const Word &word1, const Word &word2) -> Phrase +{ + return Phrase{ word1, word2 }; +}; + +auto objectDeclaration(Phrase &&objectMember, QLatin1StringView objName) -> Phrase +{ + return Phrase{} << Word(objName) << Word(Token::T_LBRACE) << std::move(objectMember) + << Word(Token::T_RBRACE); +} + +} // namespace Syntax QT_END_NAMESPACE diff --git a/src/quicktestutils/qml/qmlutils_p.h b/src/quicktestutils/qml/qmlutils_p.h index d8d28256ac..d4fc83c270 100644 --- a/src/quicktestutils/qml/qmlutils_p.h +++ b/src/quicktestutils/qml/qmlutils_p.h @@ -22,6 +22,7 @@ #include <QtCore/QStringList> #include <QtTest/QTest> #include <QtCore/private/qglobal_p.h> +#include <private/qqmljsgrammar_p.h> QT_BEGIN_NAMESPACE @@ -122,6 +123,59 @@ void gc(QV4::ExecutionEngine &engine, GCFlags flags = GCFlags::None); bool gcDone(QQmlEngine *engine); void gc(QQmlEngine &engine, GCFlags flags = GCFlags::None); +namespace Syntax { +using Token = QQmlJSGrammar::VariousConstants; +// TODO(QTBUG-138020) +constexpr auto spellFor(Token token) -> QLatin1StringView +{ + switch (token) { + case Token::T_COLON: + return QLatin1StringView(":"); + case Token::T_LBRACE: + return QLatin1StringView("{"); + case Token::T_RBRACE: + return QLatin1StringView("}"); + case Token::T_VAR: + return QLatin1StringView("var"); + case Token::T_PROPERTY: + return QLatin1StringView("property"); + case Token::T_DEFAULT: + return QLatin1StringView("default"); + case Token::T_READONLY: + return QLatin1StringView("readonly"); + case Token::T_REQUIRED: + return QLatin1StringView("required"); + case Token::T_FINAL: + return QLatin1StringView("final"); + case Token::T_VIRTUAL: + return QLatin1StringView("virtual"); + case Token::T_OVERRIDE: + return QLatin1StringView("override"); + default: + break; + } + Q_UNREACHABLE_RETURN({}); +} + +using Word = std::variant<Token, QLatin1StringView>; +auto stringView(const Word &word) -> QLatin1StringView; + +using Phrase = QList<Word>; +auto toString(const Phrase &phrase) -> QString; + +// comfort +auto operator+(const Word &word, const Phrase &phrase) -> Phrase; +auto operator+(const Word &word1, const Word &word2) -> Phrase; + +// if such declaration generating functions start to grow it might be worth creating a dedicated +// more optimized generator + +// can probably be generalized later to accept QList<Phrase> if needed +auto objectDeclaration(Phrase &&objectMember = {}, + QLatin1StringView objName = QLatin1StringView("QtObject")) -> Phrase; + +} // namespace Syntax + QT_END_NAMESPACE #endif // QQMLTESTUTILS_P_H |
