aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qml/Qt6QmlMacros.cmake40
-rw-r--r--src/qml/common/qv4compileddata.cpp38
-rw-r--r--src/qml/common/qv4compileddata_p.h24
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp10
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h2
-rw-r--r--src/qml/compiler/qv4compiler.cpp3
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc13
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference.cpp29
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference_p.h1
-rw-r--r--src/qml/qml/qqmlcomponentandaliasresolver_p.h25
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp10
-rw-r--r--src/qml/qml/qqmlproperty.cpp18
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp238
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h24
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h69
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h34
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp48
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h4
-rw-r--r--src/qml/qml/qqmltypedata.cpp125
-rw-r--r--src/qml/qml/qqmltypedata_p.h3
-rw-r--r--src/qml/qml/qqmltypeloader.cpp1
-rw-r--r--src/qml/qml/qqmltypeloader_p.h38
-rw-r--r--src/qml/qml/qqmltypeloaderdata_p.h2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp26
-rw-r--r--src/qmldom/qqmldomelements.cpp14
-rw-r--r--src/qmldom/qqmldomelements_p.h1
-rw-r--r--src/qmldom/qqmldomfieldfilter.cpp1
-rw-r--r--src/qmldom/qqmldomlinewriter.cpp7
-rw-r--r--src/qmldom/qqmldomlinewriter_p.h6
-rw-r--r--src/qmldom/qqmldomreformatter.cpp133
-rw-r--r--src/qmldom/qqmldomreformatter_p.h21
-rw-r--r--src/qmlformat/qqmlformatoptions.cpp20
-rw-r--r--src/qmlformat/qqmlformatoptions_p.h11
-rw-r--r--src/qmlformat/qqmlformatsettings.cpp1
-rw-r--r--src/qmlformat/qqmlformatsettings_p.h1
-rw-r--r--src/qmlls/qqmllsutils.cpp19
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp2
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp19
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h3
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp33
-rw-r--r--src/quicktemplates/qquickmenu.cpp17
-rw-r--r--src/quickvectorimage/generator/qquicknodeinfo_p.h9
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator.cpp13
44 files changed, 592 insertions, 566 deletions
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index c9aebff06d..b0eb863ddf 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -914,6 +914,13 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
endif()
endif()
+ # Ensure that a top-level target-tooling that accumulates the target sources
+ # Before 3.19 interface targets could not have sources, so a different approach is used
+ # for that.
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19)
+ _qt_internal_ensure_tooling_target("${target}_tooling" DEPENDENT_TARGET "${target}")
+ endif()
+
set(cache_target)
qt6_target_qml_sources(${target}
__QT_INTERNAL_FORCE_DEFER_QMLDIR
@@ -924,6 +931,20 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
)
list(APPEND output_targets ${cache_target})
+ # Setup the qml/resource files copy instructions
+ _qt_internal_qml_copy_files_to_build_dir("${target}"
+ CUSTOM_TARGET_SUFFIX qml
+ PROP_WITH_ENTRIES _qt_qml_files_to_copy
+ PROP_WITH_SRCS _qt_qml_files_absolute_src_paths
+ FILE_TYPE "sources"
+ )
+ _qt_internal_qml_copy_files_to_build_dir("${target}"
+ CUSTOM_TARGET_SUFFIX res
+ PROP_WITH_ENTRIES _qt_qml_resources_to_copy
+ PROP_WITH_SRCS _qt_qml_resources_absolute_src_paths
+ FILE_TYPE "resources"
+ )
+
# Build an init object library for static plugins and propagate it along with the plugin
# target.
# TODO: Figure out if we can move this code block into qt_add_qml_plugin. Need to consider
@@ -2798,7 +2819,10 @@ function(_qt_internal_qml_copy_files_to_build_dir target)
# Protect against multiple calls of qt6_add_qml_sources. It's enough to setup once,
# because we use generator expressions to get all the files.
if(copy_files_setup_done)
- return()
+ message(FATAL_ERROR
+ "_qt_internal_qml_copy_files_to_build_dir was called more than once for "
+ "target ${target} CUSTOM_TARGET_SUFFIX ${arg_CUSTOM_TARGET_SUFFIX}."
+ )
endif()
set_property(TARGET "${target}" PROPERTY "${setup_done_prop}" TRUE)
@@ -3585,7 +3609,6 @@ function(qt6_target_qml_sources target)
# system target from CMake 3.19 onward, and we can add the sources
# progressively over multiple calls.
set(tooling_target "${target}_tooling")
- _qt_internal_ensure_tooling_target("${tooling_target}" DEPENDENT_TARGET "${target}")
# If the tooling target was created in another directory scope, we must create another
# interface library in this directory scope to drive the custom target. A dependency
@@ -3615,19 +3638,6 @@ function(qt6_target_qml_sources target)
endif()
endif()
- _qt_internal_qml_copy_files_to_build_dir("${target}"
- CUSTOM_TARGET_SUFFIX qml
- PROP_WITH_ENTRIES _qt_qml_files_to_copy
- PROP_WITH_SRCS _qt_qml_files_absolute_src_paths
- FILE_TYPE "sources"
- )
- _qt_internal_qml_copy_files_to_build_dir("${target}"
- CUSTOM_TARGET_SUFFIX res
- PROP_WITH_ENTRIES _qt_qml_resources_to_copy
- PROP_WITH_SRCS _qt_qml_resources_absolute_src_paths
- FILE_TYPE "resources"
- )
-
# Batch all the non-compiled qml sources into a single resource for this
# call. Subsequent calls for the same target will be in their own separate
# resource file.
diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp
index 9ce5f5058f..4fbd05d060 100644
--- a/src/qml/common/qv4compileddata.cpp
+++ b/src/qml/common/qv4compileddata.cpp
@@ -50,29 +50,6 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString)
return true;
}
-/*!
- \internal
- This function creates a temporary key vector and sorts it to guarantuee a stable
- hash. This is used to calculate a check-sum on dependent meta-objects.
- */
-bool ResolvedTypeReferenceMap::addToHash(
- QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
-{
- std::vector<int> keys (size());
- int i = 0;
- for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
- keys[i] = it.key();
- ++i;
- }
- std::sort(keys.begin(), keys.end());
- for (int key: keys) {
- if (!this->operator[](key)->addToHash(hash, checksums))
- return false;
- }
-
- return true;
-}
-
CompilationUnit::CompilationUnit(
const Unit *unitData, const QString &fileName, const QString &finalUrlString)
{
@@ -354,21 +331,6 @@ int CompilationUnit::totalParserStatusCount(const QString &inlineComponentRootNa
return inlineComponentData[inlineComponentRootName].totalParserStatusCount;
}
-bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
-{
- if (!dependencyHasher) {
- for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
- if (data->dependencyMD5Checksum[i] != 0)
- return false;
- }
- return true;
- }
- const QByteArray checksum = dependencyHasher();
- return checksum.size() == sizeof(data->dependencyMD5Checksum)
- && memcmp(data->dependencyMD5Checksum, checksum.constData(),
- sizeof(data->dependencyMD5Checksum)) == 0;
-}
-
QQmlType CompilationUnit::qmlTypeForComponent(const QString &inlineComponentName) const
{
if (inlineComponentName.isEmpty())
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 889df101d0..3e29ec7aef 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -51,7 +51,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 0x45 // Removed Qt version and compile hash checks
+#define QV4_DATA_STRUCTURE_VERSION 0x46 // Removed property indices from aliases
class QIODevice;
class QQmlTypeNameCache;
@@ -85,10 +85,7 @@ namespace CompiledData {
using BindingPropertyData = QVector<const QQmlPropertyData *>;
// map from name index
-struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
-{
- bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
-};
+using ResolvedTypeReferenceMap = QHash<int, ResolvedTypeReference*>;
struct String;
struct Function;
@@ -871,8 +868,8 @@ public:
enum Flag : unsigned int {
IsReadOnly = 0x1,
- Resolved = 0x2,
- AliasPointsToPointerObject = 0x4
+ AliasPointsToPointerObject = 0x2,
+ // One bit vacant
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -882,7 +879,6 @@ public:
union {
quint32_le propertyNameIndex; // string index
- qint32_le encodedMetaPropertyIndex;
quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
};
Location location;
@@ -913,12 +909,6 @@ public:
nameIndexAndFlags.set<NameIndexField>(nameIndex);
}
- bool isObjectAlias() const
- {
- Q_ASSERT(hasFlag(Resolved));
- return encodedMetaPropertyIndex == -1;
- }
-
quint32 idIndex() const
{
return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<IdIndexField>();
@@ -1217,7 +1207,7 @@ struct Unit
quint32_le unitSize; // Size of the Unit and any depending data.
char md5Checksum[16]; // checksum of all bytes following this field.
- char dependencyMD5Checksum[16];
+ char reserved2[16]; // Was dependency checksum. 0 means "No checksum needed" to old versions.
enum : unsigned int {
IsJavascript = 0x1,
@@ -1449,8 +1439,6 @@ struct TypeReferenceMap : QHash<int, TypeReference>
}
};
-using DependentTypesHasher = std::function<QByteArray()>;
-
struct InlineComponentData {
InlineComponentData() = default;
@@ -1681,8 +1669,6 @@ public:
void finalizeCompositeType(const QQmlType &type);
- bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
-
enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault };
ListPropertyAssignBehavior listPropertyAssignBehavior() const
{
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index cdfa698ff6..094df48a2a 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -1645,7 +1645,7 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope
return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
}
-void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+void QmlUnitGenerator::generate(Document &output)
{
using namespace QV4::CompiledData;
@@ -1740,14 +1740,6 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
}
}
- if (dependencyHasher) {
- const QByteArray checksum = dependencyHasher();
- if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) {
- memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(),
- sizeof(createdUnit->dependencyMD5Checksum));
- }
- }
-
createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
}
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index d4cdcf62f0..36c9c33561 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -622,7 +622,7 @@ public:
struct Q_QML_COMPILER_EXPORT QmlUnitGenerator
{
- void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
+ void generate(Document &output);
private:
typedef bool (Binding::*BindingFilter)() const;
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index c2c7426bd2..2ef93cf7d2 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -632,8 +632,9 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.flags = QV4::CompiledData::Unit::IsJavascript;
unit.flags |= module->unitFlags;
unit.version = QV4_DATA_STRUCTURE_VERSION;
+ unit.reserved = 0;
memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
- memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
+ memset(unit.reserved2, 0, sizeof(unit.reserved2));
quint32 nextOffset = sizeof(CompiledData::Unit);
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc
index fbf7306a3d..681385a08a 100644
--- a/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc
@@ -101,7 +101,10 @@ The following options are available:
\li --functions-spacing
\li
\li Ensure spaces between functions (only works with normalize option).
-
+\row
+ \li --semicolon-rule
+ \li always
+ \li Customize the addition of semicolons at the end of JS statements.
\endtable
\section2 Arguments
@@ -155,4 +158,12 @@ the valid file entries in place.
\warning If you provide -F option, qmlformat will ignore the positional arguments.
+\section3 Semicolon Rule
+The \c{--semicolon-rule} option allows you to customize the addition of semicolons at the end of JS statements.
+The following values are accepted:
+\list
+ \li \c{always} - Always add semicolons (default).
+ \li \c{essential} - Remove semicolons unless omitting them would cause issues.
+\endlist
+
*/
diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp
index 7dcf2cd0b8..0f55660048 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference.cpp
+++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp
@@ -64,35 +64,6 @@ QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache()
}
}
-bool ResolvedTypeReference::addToHash(
- QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums)
-{
- if (m_type.isInlineComponentType()) {
-
- // A reference to an inline component in the same file will have
- // - no compilation unit since we cannot resolve the compilation unit before it's built.
- // - a property cache since we've assigned one in buildMetaObjectsIncrementally().
- // - a QQmlType that says it's an inline component.
- // We don't have to add such a thing to the hash since if it changes, the QML document
- // itself changes, leading to a new timestamp, which is checked before the checksum.
- if (!m_compilationUnit)
- return !m_typePropertyCache.isNull();
-
- } else if (m_type.isValid()) {
- bool ok = false;
- if (QQmlPropertyCache::ConstPtr propertyCache = createPropertyCache())
- hash->addData(propertyCache->checksum(checksums, &ok));
- else
- Q_ASSERT(m_type.module() == QLatin1String("QML")); // a builtin without metaobject
- return ok;
- }
- if (!m_compilationUnit)
- return false;
- hash->addData({m_compilationUnit->unitData()->md5Checksum,
- sizeof(m_compilationUnit->unitData()->md5Checksum)});
- return true;
-}
-
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h
index 4aaafdd71c..fce71ea0ad 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference_p.h
+++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h
@@ -39,7 +39,6 @@ public:
QQmlPropertyCache::ConstPtr propertyCache() const;
QQmlPropertyCache::ConstPtr createPropertyCache();
- bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums);
void doDynamicTypeCheck();
diff --git a/src/qml/qml/qqmlcomponentandaliasresolver_p.h b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
index 50d65ddb37..528a12b1c2 100644
--- a/src/qml/qml/qqmlcomponentandaliasresolver_p.h
+++ b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
@@ -63,7 +63,20 @@ private:
void setObjectId(int index) const;
[[nodiscard]] bool markAsComponent(int index) const;
[[nodiscard]] AliasResolutionResult resolveAliasesInObject(
- const CompiledObject &component, int objectIndex, QQmlError *error);
+ const CompiledObject &component, int objectIndex,
+ QQmlPropertyCacheAliasCreator<ObjectContainer> *aliasCacheCreator, QQmlError *error);
+ [[nodiscard]] bool appendAliasToPropertyCache(
+ const CompiledObject *component, const QV4::CompiledData::Alias *alias, int objectIndex,
+ int aliasIndex, int encodedPropertyIndex,
+ QQmlPropertyCacheAliasCreator<ObjectContainer> *aliasCacheCreator, QQmlError *error)
+ {
+ *error = aliasCacheCreator->appendAliasToPropertyCache(
+ *component, *alias, objectIndex, aliasIndex, encodedPropertyIndex);
+ resolvedAliases.insert(alias);
+ return !error->isValid();
+ }
+
+
void resolveGeneralizedGroupProperty(const CompiledObject &component, CompiledBinding *binding);
[[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
@@ -122,6 +135,7 @@ private:
QVector<quint32> m_componentRoots;
QVector<int> m_objectsWithAliases;
QVector<CompiledBinding *> m_generalizedGroupProperties;
+ QSet<const QV4::CompiledData::Alias *> resolvedAliases;
typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
};
@@ -435,15 +449,12 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int com
QQmlError error;
const auto &component = *m_compiler->objectAt(componentIndex);
- const auto result = resolveAliasesInObject(component, objectIndex, &error);
+ const auto result
+ = resolveAliasesInObject(component, objectIndex, &aliasCacheCreator, &error);
if (error.isValid())
return error;
if (result == AllAliasesResolved) {
- QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
- component, objectIndex);
- if (error.isValid())
- return error;
atLeastOneAliasResolved = true;
} else if (result == SomeAliasesResolved) {
atLeastOneAliasResolved = true;
@@ -458,7 +469,7 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int com
if (!atLeastOneAliasResolved && !m_objectsWithAliases.isEmpty()) {
const CompiledObject *obj = m_compiler->objectAt(m_objectsWithAliases.first());
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved))
+ if (!resolvedAliases.contains(alias))
return error(alias->location, QQmlComponentAndAliasResolverBase::tr("Circular alias reference detected"));
}
}
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 1e8a5f855a..90ee916fe1 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -1842,7 +1842,6 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
const auto originalAlias = alias;
while (alias->isAliasToLocalAlias())
alias = _compiledObject->aliasesBegin() + alias->localAliasIndex;
- Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
if (!context->isIdValueSet(0)) // TODO: Do we really want 0 here?
continue;
QObject *target = context->idValue(alias->targetObjectId());
@@ -1851,7 +1850,14 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
QQmlData *targetDData = QQmlData::get(target, /*create*/false);
if (targetDData == nullptr || targetDData->propertyCache.isNull())
continue;
- int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+
+ const QQmlPropertyData *aliasProperty =
+ _propertyCache->property(_vmeMetaObject->aliasOffset() + aliasIndex);
+ if (!aliasProperty)
+ continue;
+ const int targetPropertyIndex = aliasProperty->aliasTarget();
+ int coreIndex = QQmlPropertyIndex::fromEncoded(targetPropertyIndex).coreIndex();
+
const QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(coreIndex);
if (!targetProperty)
continue;
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index e56ead94f9..59a26bb630 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1153,10 +1153,22 @@ QVariant QQmlPropertyPrivate::readValueProperty()
}
return QVariant();
} else if (core.isQList()) {
+ auto coreMetaType = core.propType();
- QQmlListProperty<QObject> prop;
- core.readProperty(object, &prop);
- return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType()));
+ // IsQmlList is set for QQmlListPropery and list<ObjectType>
+ if (coreMetaType.flags() & QMetaType::IsQmlList) {
+ QQmlListProperty<QObject> prop;
+ core.readProperty(object, &prop);
+ return QVariant::fromValue(QQmlListReferencePrivate::init(prop, coreMetaType));
+ } else {
+ // but not for lists of value types
+ QVariant result(coreMetaType);
+ // TODO: ideally, we would not default construct and copy assign,
+ // but do a single copy-construct; we don't have API for that, though
+ coreMetaType.construct(result.data());
+ core.readProperty(object, result.data());
+ return result;
+ }
} else if (core.isQObject()) {
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index e33381c84c..31f9af3375 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -221,18 +221,23 @@ void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Fl
data.setFlags(flags);
data.setTypeVersion(version);
- QQmlPropertyData *old = findNamedProperty(name);
- const OverrideResult overrideResult = handleOverride(name, &data, old);
- if (overrideResult == InvalidOverride) {
- // Insert the overridden member once more, to keep the counts in sync
- propertyIndexCache.append(*old);
- return;
- }
+ doAppendPropertyData(name, std::move(data));
+}
- int index = propertyIndexCache.size();
- propertyIndexCache.append(data);
+void QQmlPropertyCache::appendAlias(
+ const QString &name, QQmlPropertyData::Flags flags, int coreIndex, QMetaType propType,
+ QTypeRevision version, int notifyIndex, int encodedTargetIndex)
+{
+ QQmlPropertyData data;
+ data.setPropType(propType);
+ data.setCoreIndex(coreIndex);
+ data.setNotifyIndex(notifyIndex);
+ flags.setIsAlias(true);
+ data.setFlags(flags);
+ data.setAliasTarget(encodedTargetIndex);
+ data.setTypeVersion(version);
- setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index);
+ doAppendPropertyData(name, std::move(data));
}
void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flags flags,
@@ -569,7 +574,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
// otherwise always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
- if (!isGadget)
+ if (!isGadget && !data->isAlias())
data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
}
}
@@ -922,15 +927,6 @@ const QQmlPropertyData *QQmlPropertyCache::property(
return qQmlPropertyCacheProperty<const QLatin1String &>(obj, name, context, local);
}
-// this function is copied from qmetaobject.cpp
-static inline const QByteArray stringData(const QMetaObject *mo, int index)
-{
- uint offset = mo->d.stringdata[2*index];
- uint length = mo->d.stringdata[2*index + 1];
- const char *string = reinterpret_cast<const char *>(mo->d.stringdata) + offset;
- return QByteArray::fromRawData(string, length);
-}
-
const char *QQmlPropertyCache::className() const
{
if (const QMetaObject *mo = _metaObject.metaObject())
@@ -1073,209 +1069,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const
builder.addClassInfo("QML.ListPropertyAssignBehavior", _listPropertyAssignBehavior);
}
-namespace {
-template <typename StringVisitor, typename TypeInfoVisitor>
-int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount,
- StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
-{
- int fieldsForParameterData = 0;
-
- bool hasOldStyleRevisionedMethods = false;
-
- for (int i = 0; i < methodCount; ++i) {
- const int handle = methodOffset + i * QMetaObjectPrivate::IntsPerMethod;
-
- const uint flags = mo.d.data[handle + 4];
- if (flags & MethodRevisioned) {
- if (mo.d.data[0] < 13)
- hasOldStyleRevisionedMethods = true;
- else
- fieldsForParameterData += 1; // revision
- }
-
- visitString(mo.d.data[handle + 0]); // name
- visitString(mo.d.data[handle + 3]); // tag
-
- const int argc = mo.d.data[handle + 1];
- const int paramIndex = mo.d.data[handle + 2];
-
- fieldsForParameterData += argc * 2; // type and name
- fieldsForParameterData += 1; // + return type
-
- // return type + args
- for (int i = 0; i < 1 + argc; ++i) {
- // type name (maybe)
- visitTypeInfo(mo.d.data[paramIndex + i]);
-
- // parameter name
- if (i > 0)
- visitString(mo.d.data[paramIndex + argc + i]);
- }
- }
-
- int fieldsForRevisions = 0;
- if (hasOldStyleRevisionedMethods)
- fieldsForRevisions = methodCount;
-
- return methodCount * QMetaObjectPrivate::IntsPerMethod
- + fieldsForRevisions + fieldsForParameterData;
-}
-
-template <typename StringVisitor, typename TypeInfoVisitor>
-int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
-{
- const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
-
- for (int i = 0; i < priv->propertyCount; ++i) {
- const int handle = priv->propertyData + i * QMetaObjectPrivate::IntsPerProperty;
-
- visitString(mo.d.data[handle]); // name
- visitTypeInfo(mo.d.data[handle + 1]);
- }
-
- return priv->propertyCount * QMetaObjectPrivate::IntsPerProperty;
-}
-
-template <typename StringVisitor>
-int visitClassInfo(const QMetaObject &mo, StringVisitor visitString)
-{
- const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- const int intsPerClassInfo = 2;
-
- for (int i = 0; i < priv->classInfoCount; ++i) {
- const int handle = priv->classInfoData + i * intsPerClassInfo;
-
- visitString(mo.d.data[handle]); // key
- visitString(mo.d.data[handle + 1]); // value
- }
-
- return priv->classInfoCount * intsPerClassInfo;
-}
-
-template <typename StringVisitor>
-int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
-{
- const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
-
- int fieldCount = priv->enumeratorCount * QMetaObjectPrivate::IntsPerEnum;
-
- for (int i = 0; i < priv->enumeratorCount; ++i) {
- const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * QMetaObjectPrivate::IntsPerEnum;
-
- const uint keyCount = enumeratorData[3];
- fieldCount += keyCount * 2;
-
- visitString(enumeratorData[0]); // name
- visitString(enumeratorData[1]); // enum name
-
- const uint keyOffset = enumeratorData[4];
-
- for (uint j = 0; j < keyCount; ++j) {
- visitString(mo.d.data[keyOffset + 2 * j]);
- }
- }
-
- return fieldCount;
-}
-
-template <typename StringVisitor>
-int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
-{
- const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
-
- const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) {
- if (typeInfo & IsUnresolvedType)
- stringVisitor(typeInfo & TypeNameIndexMask);
- };
-
- int fieldCount = MetaObjectPrivateFieldCount;
-
- fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor,
- typeInfoVisitor);
- fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor,
- typeInfoVisitor);
-
- fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor);
- fieldCount += visitClassInfo(mo, stringVisitor);
- fieldCount += visitEnumerations(mo, stringVisitor);
-
- return fieldCount;
-}
-
-} // anonymous namespace
-
-static_assert(QMetaObjectPrivate::OutputRevision == 13, "Check and adjust determineMetaObjectSizes");
-
-bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
- int *stringCount)
-{
- const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- if (priv->revision != QMetaObjectPrivate::OutputRevision)
- return false;
-
- uint highestStringIndex = 0;
- const auto stringIndexVisitor = [&highestStringIndex](uint index) {
- highestStringIndex = qMax(highestStringIndex, index);
- };
-
- *fieldCount = countMetaObjectFields(mo, stringIndexVisitor);
- *stringCount = highestStringIndex + 1;
-
- return true;
-}
-
-bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo)
-{
- int fieldCount = 0;
- int stringCount = 0;
- if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) {
- return false;
- }
- hash.addData({reinterpret_cast<const char *>(mo.d.data), qsizetype(fieldCount * sizeof(uint))});
- for (int i = 0; i < stringCount; ++i) {
- hash.addData(stringData(&mo, i));
- }
-
- return true;
-}
-
-QByteArray QQmlPropertyCache::checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const
-{
- auto it = checksums->constFind(quintptr(this));
- if (it != checksums->constEnd()) {
- *ok = true;
- return *it;
- }
-
- // Generate a checksum on the meta-object data only on C++ types.
- if (_metaObject.isShared()) {
- *ok = false;
- return QByteArray();
- }
-
- QCryptographicHash hash(QCryptographicHash::Md5);
-
- if (_parent) {
- hash.addData(_parent->checksum(checksums, ok));
- if (!*ok)
- return QByteArray();
- }
-
- if (!addToHash(hash, *_metaObject.metaObject())) {
- *ok = false;
- return QByteArray();
- }
-
- const QByteArray result = hash.result();
- if (result.isEmpty()) {
- *ok = false;
- } else {
- *ok = true;
- checksums->insert(quintptr(this), result);
- }
- return result;
-}
/*! \internal
\a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 24d00c030f..5b75bf48f3 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -153,6 +153,9 @@ public:
int propertyCount, int methodCount, int signalCount, int enumCount) const;
void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
QMetaType propType, QTypeRevision revision, int notifyIndex);
+ void appendAlias(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
+ QMetaType propType, QTypeRevision version, int notifyIndex,
+ int encodedTargetIndex);
void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex,
const QMetaType *types = nullptr,
const QList<QByteArray> &names = QList<QByteArray>());
@@ -224,11 +227,6 @@ public:
inline bool callJSFactoryMethod(QObject *object, void **args) const;
- static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
- static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
-
- QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const;
-
QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; }
void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; }
@@ -296,6 +294,22 @@ private:
return handleOverride(name, data, findNamedProperty(name));
}
+ void doAppendPropertyData(const QString &name, QQmlPropertyData &&data)
+ {
+ QQmlPropertyData *old = findNamedProperty(name);
+ const OverrideResult overrideResult = handleOverride(name, &data, old);
+ if (overrideResult == InvalidOverride) {
+ // Insert the overridden member once more, to keep the counts in sync
+ propertyIndexCache.append(*old);
+ return;
+ }
+
+ const int index = propertyIndexCache.size();
+ propertyIndexCache.append(std::move(data));
+
+ setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index);
+ }
+
int propertyIndexCacheStart = 0; // placed here to avoid gap between QQmlRefCount and _parent
QQmlPropertyCache::ConstPtr _parent;
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index d240cc179b..2d60504d8d 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -873,12 +873,15 @@ public:
QQmlPropertyCacheAliasCreator(
QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
- QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+ QQmlError appendAliasToPropertyCache(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int objectIndex,
+ int aliasIndex, int encodedMetaPropertyIndex);
private:
QQmlError propertyDataForAlias(
const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
- QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags);
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
+ int targetPropertyIndex);
QQmlPropertyCacheVector *propertyCaches;
const ObjectContainer *objectContainer;
@@ -895,7 +898,7 @@ inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCre
template <typename ObjectContainer>
inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
- QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags)
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags, int targetPropertyIndex)
{
*type = QMetaType();
bool writable = false;
@@ -929,14 +932,15 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
lastAlias = targetAlias;
} while (lastAlias->isAliasToLocalAlias());
- return propertyDataForAlias(component, *lastAlias, type, version, propertyFlags);
+ return propertyDataForAlias(
+ component, *lastAlias, type, version, propertyFlags, targetPropertyIndex);
}
const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
- if (alias.encodedMetaPropertyIndex == -1) {
+ if (targetPropertyIndex == -1) {
Q_ASSERT(alias.hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject));
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
if (!typeRef) {
@@ -962,9 +966,9 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
propertyFlags->setType(QQmlPropertyData::Flags::QObjectDerivedType);
} else {
- int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
- int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
- alias.encodedMetaPropertyIndex).valueTypeIndex();
+ int coreIndex = QQmlPropertyIndex::fromEncoded(targetPropertyIndex).coreIndex();
+ int valueTypeIndex
+ = QQmlPropertyIndex::fromEncoded(targetPropertyIndex).valueTypeIndex();
QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
@@ -1040,41 +1044,36 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
}
template <typename ObjectContainer>
-inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
- const CompiledObject &component, int objectIndex)
+inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasToPropertyCache(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int objectIndex,
+ int aliasIndex, int encodedMetaPropertyIndex)
{
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
- if (!object.aliasCount())
- return QQmlError();
- QQmlPropertyCache::Ptr propertyCache = propertyCaches->ownAt(objectIndex);
- Q_ASSERT(propertyCache);
-
- int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.size();
- int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.size();
+ Q_ASSERT(object.aliasCount() > aliasIndex);
+ QMetaType type;
+ QTypeRevision version = QTypeRevision::zero();
+ QQmlPropertyData::Flags propertyFlags;
+ QQmlError error = propertyDataForAlias(
+ component, alias, &type, &version, &propertyFlags, encodedMetaPropertyIndex);
+ if (error.isValid())
+ return error;
- int aliasIndex = 0;
- auto alias = object.aliasesBegin();
- auto end = object.aliasesEnd();
- for ( ; alias != end; ++alias, ++aliasIndex) {
- Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
-
- QMetaType type;
- QTypeRevision version = QTypeRevision::zero();
- QQmlPropertyData::Flags propertyFlags;
- QQmlError error = propertyDataForAlias(component, *alias, &type, &version, &propertyFlags);
- if (error.isValid())
- return error;
+ const QString propertyName = objectContainer->stringAt(alias.nameIndex());
- const QString propertyName = objectContainer->stringAt(alias->nameIndex());
+ const QQmlPropertyCache::Ptr propertyCache = propertyCaches->ownAt(objectIndex);
+ Q_ASSERT(propertyCache);
- if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
- propertyCache->_defaultPropertyName = propertyName;
+ const int effectiveSignalIndex
+ = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.size();
+ const int effectivePropertyIndex
+ = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.size();
- propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- type, version, effectiveSignalIndex++);
- }
+ if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+ propertyCache->appendAlias(propertyName, propertyFlags, effectivePropertyIndex,
+ type, version, effectiveSignalIndex, encodedMetaPropertyIndex);
return QQmlError();
}
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index 4093617364..d19937d672 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -209,7 +209,6 @@ public:
bool isFinal() const { return !isFunction() && m_flags.isFinalORisV4Function; }
bool isOverridden() const { return m_flags.isOverridden; }
bool isRequired() const { return !isFunction() && m_flags.isRequiredORisCloned; }
- bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
bool isFunction() const { return m_flags.type == Flags::FunctionType; }
bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
bool isEnum() const { return m_flags.type == Flags::EnumType; }
@@ -224,6 +223,11 @@ public:
bool isSignalHandler() const { return m_flags.isSignalHandler; }
bool hasMetaObject() const { return m_flags.hasMetaObject; }
+ bool hasStaticMetaCallFunction() const
+ {
+ return !isAlias() && staticMetaCallFunction() != nullptr;
+ }
+
// TODO: Remove this once we can. Signals should not be overridable.
bool isOverridableSignal() const { return m_flags.isOverridableSignal; }
@@ -269,6 +273,17 @@ public:
m_coreIndex = qint16(idx);
}
+ int aliasTarget() const
+ {
+ Q_ASSERT(isAlias());
+ return m_encodedAliasTargetIndex;
+ }
+ void setAliasTarget(int target)
+ {
+ Q_ASSERT(isAlias());
+ m_encodedAliasTargetIndex = target;
+ }
+
QTypeRevision revision() const { return m_revision; }
void setRevision(QTypeRevision revision) { m_revision = revision; }
@@ -332,10 +347,15 @@ public:
m_metaObjectOffset = qint16(off);
}
- StaticMetaCallFunction staticMetaCallFunction() const { Q_ASSERT(!isFunction()); return m_staticMetaCallFunction; }
+ StaticMetaCallFunction staticMetaCallFunction() const
+ {
+ Q_ASSERT(!isFunction() && !isAlias());
+ return m_staticMetaCallFunction;
+ }
+
void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
{
- Q_ASSERT(!isFunction());
+ Q_ASSERT(!isFunction() && !isAlias());
if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
m_flags.otherBits = relativePropertyIndex;
m_staticMetaCallFunction = f;
@@ -452,9 +472,17 @@ private:
QMetaType m_propType = {};
union {
+ // only for methods (if stored in property cache)
QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
+
+ // only for C++-declared properties
StaticMetaCallFunction m_staticMetaCallFunction;
+
+ // only for methods (if stored in lookups)
const QMetaObject *m_metaObject;
+
+ // only for aliases
+ int m_encodedAliasTargetIndex;
};
};
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 842b63b12d..4f091eafe8 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -26,11 +26,9 @@ Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler");
QQmlTypeCompiler::QQmlTypeCompiler(
QQmlTypeLoader *typeLoader, QQmlTypeData *typeData, QmlIR::Document *parsedQML,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache)
: resolvedTypes(resolvedTypeCache)
, loader(typeLoader)
- , dependencyHasher(dependencyHasher)
, document(parsedQML)
, typeData(typeData)
{
@@ -138,7 +136,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- qmlGenerator.generate(*document, dependencyHasher);
+ qmlGenerator.generate(*document);
if (!errors.isEmpty())
return nullptr;
@@ -831,22 +829,30 @@ void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupPro
template<>
typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
- const CompiledObject &component, int objectIndex, QQmlError *error)
+ const CompiledObject &component, int objectIndex,
+ QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> *aliasCacheCreator, QQmlError *error)
{
+ // TODO: This method should not modify the aliases themselves. Rather, all information
+ // needed for handling them later should be stored in the property cache.
+ // Some of the information calculated here could be calculated already at compile time.
+ // See QTBUG-136572.
+
Q_UNUSED(component);
const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex);
if (!obj->aliasCount())
return AllAliasesResolved;
- int numResolvedAliases = 0;
- bool seenUnresolvedAlias = false;
+ int aliasIndex = 0;
+ int numSkippedAliases = 0;
for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
- if (alias->hasFlag(QV4::CompiledData::Alias::Resolved))
+ if (resolvedAliases.contains(alias)) {
+ ++aliasIndex;
+ ++numSkippedAliases;
continue;
+ }
- seenUnresolvedAlias = true;
const int idIndex = alias->idIndex();
const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
@@ -906,14 +912,19 @@ QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
if (targetObjectIndex == objectIndex) {
alias->localAliasIndex = localAliasIndex;
alias->setIsAliasToLocalAlias(true);
- alias->setFlag(QV4::CompiledData::Alias::Resolved);
- ++numResolvedAliases;
+ if (!appendAliasToPropertyCache(
+ &component, alias, objectIndex, aliasIndex++, -1,
+ aliasCacheCreator, error)) {
+ break;
+ }
continue;
}
// restore
alias->setIdIndex(idIndex);
// Try again later and resolve the target alias first.
+ ++numSkippedAliases;
+ ++aliasIndex;
break;
}
}
@@ -972,13 +983,18 @@ QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
}
}
- alias->encodedMetaPropertyIndex = propIdx.toEncoded();
- alias->setFlag(QV4::CompiledData::Alias::Resolved);
- numResolvedAliases++;
+ if (!appendAliasToPropertyCache(
+ &component, alias, objectIndex, aliasIndex++, propIdx.toEncoded(),
+ aliasCacheCreator, error)) {
+ break;
+ }
}
- if (numResolvedAliases == 0)
- return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
+ if (numSkippedAliases == aliasIndex)
+ return NoAliasResolved;
+
+ if (aliasIndex == obj->aliasCount())
+ return AllAliasesResolved;
return SomeAliasesResolved;
}
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index ecf449f549..1c14980988 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -46,8 +46,7 @@ public:
QQmlTypeCompiler(QQmlTypeLoader *typeLoader,
QQmlTypeData *typeData,
QmlIR::Document *document,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache);
// --- interface used by QQmlPropertyCacheCreator
typedef QmlIR::Object CompiledObject;
@@ -121,7 +120,6 @@ public:
private:
QList<QQmlError> errors;
QQmlTypeLoader *loader;
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher;
QmlIR::Document *document;
// index is string index of type name (use obj->inheritedTypeNameIndex)
QHash<int, QQmlCustomParser*> customParsers;
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 86b6de63b3..b07ed3b753 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -219,28 +219,104 @@ void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveG
template<>
typename QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::AliasResolutionResult
QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveAliasesInObject(
- const CompiledObject &component, int objectIndex, QQmlError *error)
+ const CompiledObject &component, int objectIndex,
+ QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> *aliasCacheCreator,
+ QQmlError *error)
{
const CompiledObject *obj = m_compiler->objectAt(objectIndex);
+ int aliasIndex = 0;
+ const auto doAppendAlias = [&](const QV4::CompiledData::Alias *alias, int encodedIndex) {
+ return appendAliasToPropertyCache(
+ &component, alias, objectIndex, aliasIndex++, encodedIndex, aliasCacheCreator,
+ error);
+ };
+
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
- *error = qQmlCompileError(alias->referenceLocation,
- QQmlComponentAndAliasResolverBase::tr("Unresolved alias found"));
- return NoAliasResolved;
+ if (resolvedAliases.contains(alias)) {
+ ++aliasIndex;
+ continue;
}
- if (alias->isAliasToLocalAlias() || alias->encodedMetaPropertyIndex == -1)
- continue;
+ if (alias->isAliasToLocalAlias()) {
+ if (doAppendAlias(alias, -1))
+ continue;
+ return SomeAliasesResolved;
+ }
const int targetObjectIndex
= objectForId(m_compiler, component, alias->targetObjectId());
- const int coreIndex
- = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+ const QV4::CompiledData::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
+
+ QStringView property;
+ QStringView subProperty;
+
+ const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
+ const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
+ if (propertySeparator != -1) {
+ property = QStringView{aliasPropertyValue}.left(propertySeparator);
+ subProperty = QStringView{aliasPropertyValue}.mid(propertySeparator + 1);
+ } else {
+ property = QStringView(aliasPropertyValue);
+ }
+ if (property.isEmpty()) {
+ if (doAppendAlias(alias, -1))
+ continue;
+ return SomeAliasesResolved;
+ }
+
+ Q_ASSERT(!property.isEmpty());
QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
- if (!targetCache->property(coreIndex))
+ const QQmlPropertyResolver resolver(targetCache);
+ const QQmlPropertyData *targetProperty = resolver.property(property.toString());
+ if (!targetProperty)
+ return SomeAliasesResolved;
+
+ const int coreIndex = targetProperty->coreIndex();
+ if (subProperty.isEmpty()) {
+ if (doAppendAlias(alias, QQmlPropertyIndex(coreIndex).toEncoded()))
+ continue;
+ return SomeAliasesResolved;
+ }
+
+ if (const QMetaObject *valueTypeMetaObject
+ = QQmlMetaType::metaObjectForValueType(targetProperty->propType())) {
+ const int valueTypeIndex = valueTypeMetaObject->indexOfProperty(
+ subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1)
+ return SomeAliasesResolved;
+
+ if (doAppendAlias(alias, QQmlPropertyIndex(coreIndex, valueTypeIndex).toEncoded()))
+ continue;
+
+ return SomeAliasesResolved;
+ }
+
+ Q_ASSERT(subProperty.at(0).isLower());
+
+ bool isDeepAlias = false;
+ for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
+ if (m_compiler->stringAt(it->propertyNameIndex) != property)
+ continue;
+
+ const QQmlPropertyResolver resolver
+ = QQmlPropertyResolver(m_propertyCaches->at(it->value.objectIndex));
+ const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
+ if (!actualProperty)
+ continue;
+
+ if (doAppendAlias(
+ alias, QQmlPropertyIndex(coreIndex, actualProperty->coreIndex()).toEncoded())) {
+ isDeepAlias = true;
+ break;
+ }
+
+ return SomeAliasesResolved;
+ }
+
+ if (!isDeepAlias)
return SomeAliasesResolved;
}
@@ -465,27 +541,16 @@ void QQmlTypeData::done()
}
}
- const auto dependencyHasher = [&resolvedTypeCache, this]() {
- return typeLoader()->hashDependencies(&resolvedTypeCache, m_compositeSingletons);
- };
-
// verify if any dependencies changed if we're using a cache
if (m_document.isNull() && verifyCaches) {
const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
- if (!error.isValid() && m_compiledData->verifyChecksum(dependencyHasher)) {
+ if (!error.isValid()) {
setCompileUnit(m_compiledData);
} else {
-
- if (error.isValid()) {
- qCDebug(DBG_DISK_CACHE)
- << "Failed to create property caches for"
- << m_compiledData->fileName()
- << "because" << error.description();
- } else {
- qCDebug(DBG_DISK_CACHE)
- << "Checksum mismatch for cached version of"
- << m_compiledData->fileName();
- }
+ qCDebug(DBG_DISK_CACHE)
+ << "Failed to create property caches for"
+ << m_compiledData->fileName()
+ << "because" << error.description();
if (!loadFromSource())
return;
@@ -515,7 +580,7 @@ void QQmlTypeData::done()
if (!m_document.isNull()) {
Q_ASSERT(verifyCaches);
// Compile component
- compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
+ compile(typeNameCache, &resolvedTypeCache);
if (isError())
return;
else
@@ -846,8 +911,7 @@ QString QQmlTypeData::stringAt(int index) const
}
void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache)
{
assertTypeLoaderThread();
@@ -859,8 +923,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
&& (m_document->javaScriptCompilationUnit->unitData()->flags
& QV4::CompiledData::Unit::PendingTypeCompilation);
- QQmlTypeCompiler compiler(
- typeLoader(), this, m_document.data(), resolvedTypeCache, dependencyHasher);
+ QQmlTypeCompiler compiler(typeLoader(), this, m_document.data(), resolvedTypeCache);
auto compilationUnit = compiler.compile();
if (!compilationUnit) {
qDeleteAll(*resolvedTypeCache);
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index ba04ee1ad2..4cd9ff64ed 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -97,8 +97,7 @@ private:
QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
) const;
void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache);
QQmlError createTypeAndPropertyCaches(
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 7420a1654c..3296b1ec1a 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1732,7 +1732,6 @@ void QQmlTypeLoader::clearCache()
QQmlTypeLoaderThreadDataPtr threadData(&m_data);
qDeleteAll(threadData->importQmlDirCache);
- threadData->checksumCache.clear();
threadData->importQmlDirCache.clear();
QQmlTypeLoaderSharedDataPtr data(&m_data);
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 47df026a07..fd8f026b59 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -44,7 +44,6 @@ class Q_QML_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
- using ChecksumCache = QQmlTypeLoaderThreadData::ChecksumCache;
enum Mode { PreferSynchronous, Asynchronous, Synchronous };
class Q_QML_EXPORT Blob : public QQmlDataBlob
@@ -161,23 +160,6 @@ public:
}
}
- // We can't include QQmlTypeData here.
- // Use a template specialized only for QQmlTypeData::TypeReference instead.
- template<typename TypeReference>
- QByteArray hashDependencies(
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QList<TypeReference> &compositeSingletons)
- {
- QQmlTypeLoaderThreadDataPtr data(&m_data);
-
- QCryptographicHash hash(QCryptographicHash::Md5);
- return (resolvedTypeCache->addToHash(&hash, &data->checksumCache)
- && addTypeReferenceChecksumsToHash(
- compositeSingletons, &data->checksumCache, &hash))
- ? hash.result()
- : QByteArray();
- }
-
static QUrl normalize(const QUrl &unNormalizedUrl);
QQmlRefPointer<QQmlTypeData> getType(
@@ -342,26 +324,6 @@ private:
void doLoad(const Loader &loader, const QQmlDataBlob::Ptr &blob, Mode mode);
void updateTypeCacheTrimThreshold(const QQmlTypeLoaderSharedDataPtr &data);
- template<typename TypeReference>
- static bool addTypeReferenceChecksumsToHash(
- const QList<TypeReference> &typeRefs,
- QHash<quintptr, QByteArray> *checksums, QCryptographicHash *hash)
- {
- for (const auto &typeRef: typeRefs) {
- if (typeRef.typeData) {
- const auto unit = typeRef.typeData->compilationUnit()->unitData();
- hash->addData({unit->md5Checksum, sizeof(unit->md5Checksum)});
- } else if (const QMetaObject *mo = typeRef.type.metaObject()) {
- const auto propertyCache = QQmlMetaType::propertyCache(mo);
- bool ok = false;
- hash->addData(propertyCache->checksum(checksums, &ok));
- if (!ok)
- return false;
- }
- }
- return true;
- }
-
QQmlMetaType::CacheMode aotCacheMode();
QQmlTypeLoaderLockedData m_data;
diff --git a/src/qml/qml/qqmltypeloaderdata_p.h b/src/qml/qml/qqmltypeloaderdata_p.h
index f9ab346cbb..f9cc20137f 100644
--- a/src/qml/qml/qqmltypeloaderdata_p.h
+++ b/src/qml/qml/qqmltypeloaderdata_p.h
@@ -58,7 +58,6 @@ class QQmlTypeLoaderThreadData
{
Q_DISABLE_COPY_MOVE(QQmlTypeLoaderThreadData)
public:
- using ChecksumCache = QHash<quintptr, QByteArray>;
using ImportQmlDirCache = QStringHash<QQmlTypeLoaderQmldirContent *>;
struct QmldirInfo {
@@ -71,7 +70,6 @@ public:
QQmlTypeLoaderThreadData() = default;
ImportQmlDirCache importQmlDirCache;
- ChecksumCache checksumCache;
// Maps from an import to a linked list of qmldir info.
// Used in locateLocalQmldir()
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index e4988a8152..1ed98a4f74 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -221,14 +221,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
int sigIdx = aliasId + metaObject->propCount();
metaObject->activate(metaObject->object, sigIdx, nullptr);
} else if (const QV4::CompiledData::Object *compiledObject = metaObject->findCompiledObject()) {
- const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
- if (!aliasData->isObjectAlias()) {
+ const QQmlPropertyData *aliasProperty
+ = metaObject->cache->property(metaObject->aliasOffset() + aliasId);
+ const int targetPropertyIndex = aliasProperty ? aliasProperty->aliasTarget() : -1;
+
+ if (targetPropertyIndex != -1) {
+ const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
+
QQmlRefPointer<QQmlContextData> ctxt = metaObject->ctxt;
QObject *target = ctxt->idValue(aliasData->targetObjectId());
if (!target)
return;
- QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(targetPropertyIndex);
int coreIndex = encodedIndex.coreIndex();
int valueTypeIndex = encodedIndex.valueTypeIndex();
const QQmlPropertyData *pd = QQmlData::ensurePropertyCache(target)->property(coreIndex);
@@ -1076,7 +1081,10 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
connectAlias(compiledObject, id);
- if (aliasData->isObjectAlias()) {
+ const QQmlPropertyData *aliasProperty = cache->property(aliasOffset() + id);
+ const int targetPropertyIndex = aliasProperty ? aliasProperty->aliasTarget() : -1;
+
+ if (targetPropertyIndex == -1) {
*reinterpret_cast<QObject **>(a[0]) = target;
return -1;
}
@@ -1085,7 +1093,8 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (!targetDData)
return -1;
- QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ QQmlPropertyIndex encodedIndex
+ = QQmlPropertyIndex::fromEncoded(targetPropertyIndex);
int coreIndex = encodedIndex.coreIndex();
const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
@@ -1433,8 +1442,11 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex,
if (!*target)
return false;
- if (!aliasData->isObjectAlias()) {
- QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ const QQmlPropertyData *aliasProperty = cache->property(aliasOffset() + aliasId);
+ const int targetPropertyIndex = aliasProperty ? aliasProperty->aliasTarget() : -1;
+
+ if (targetPropertyIndex != -1) {
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(targetPropertyIndex);
*coreIndex = encodedIndex.coreIndex();
*valueTypeIndex = encodedIndex.valueTypeIndex();
}
diff --git a/src/qmldom/qqmldomelements.cpp b/src/qmldom/qqmldomelements.cpp
index 4b18bce50f..e6bdd47799 100644
--- a/src/qmldom/qqmldomelements.cpp
+++ b/src/qmldom/qqmldomelements.cpp
@@ -1825,13 +1825,13 @@ QString ScriptExpression::astRelocatableDump() const
void ScriptExpression::writeOut(const DomItem &, OutWriter &lw) const
{
- reformatAst(
- lw, m_astComments,
- [this](SourceLocation astL) {
- SourceLocation l = this->locationToLocal(astL); // use engine->code() instead?
- return this->code().mid(l.offset, l.length);
- },
- ast());
+ reformatAst(lw, this);
+}
+
+QStringView ScriptExpression::loc2Str(SourceLocation astL) const
+{
+ SourceLocation l = this->locationToLocal(astL); // use engine->code() instead?
+ return this->code().mid(l.offset, l.length);
}
SourceLocation ScriptExpression::globalLocation(const DomItem &self) const
diff --git a/src/qmldom/qqmldomelements_p.h b/src/qmldom/qqmldomelements_p.h
index c667da93a5..c6781c6ab8 100644
--- a/src/qmldom/qqmldomelements_p.h
+++ b/src/qmldom/qqmldomelements_p.h
@@ -467,6 +467,7 @@ public:
QMutexLocker l(mutex());
return m_engine;
}
+ QStringView loc2Str(SourceLocation) const;
std::shared_ptr<AstComments> astComments() const { return m_astComments; }
void writeOut(const DomItem &self, OutWriter &lw) const override;
SourceLocation globalLocation(const DomItem &self) const;
diff --git a/src/qmldom/qqmldomfieldfilter.cpp b/src/qmldom/qqmldomfieldfilter.cpp
index 50a819414e..36bf2212e0 100644
--- a/src/qmldom/qqmldomfieldfilter.cpp
+++ b/src/qmldom/qqmldomfieldfilter.cpp
@@ -190,6 +190,7 @@ FieldFilter FieldFilter::compareNoCommentsFilter()
{ QLatin1String("QmlObject"), QLatin1String("prototypes") },
{ QLatin1String(), QLatin1String("code") },
{ QLatin1String("ScriptExpression"), QLatin1String("localOffset") },
+ { QLatin1String("ScriptExpression"), QLatin1String("astRelocatableDump") },
{ QLatin1String("FileLocationsNode"), QLatin1String("parent") },
{ QString(), QLatin1String("fileLocationsTree") },
{ QString(), QLatin1String("preCode") },
diff --git a/src/qmldom/qqmldomlinewriter.cpp b/src/qmldom/qqmldomlinewriter.cpp
index 32488fe6cc..8de38dff55 100644
--- a/src/qmldom/qqmldomlinewriter.cpp
+++ b/src/qmldom/qqmldomlinewriter.cpp
@@ -46,6 +46,13 @@ LineWriter &LineWriter::ensureSpace(TextAddType t)
return *this;
}
+LineWriter &LineWriter::ensureSemicolon(TextAddType t)
+{
+ if (!m_currentLine.isEmpty() && m_currentLine.back() != u';')
+ write(u";", t);
+ return *this;
+}
+
LineWriter &LineWriter::ensureSpace(QStringView space, TextAddType t)
{
int tabSize = m_options.formatOptions.tabSize;
diff --git a/src/qmldom/qqmldomlinewriter_p.h b/src/qmldom/qqmldomlinewriter_p.h
index a18ebc7319..f2b3d36918 100644
--- a/src/qmldom/qqmldomlinewriter_p.h
+++ b/src/qmldom/qqmldomlinewriter_p.h
@@ -81,6 +81,10 @@ public:
Q_ENUM(TrailingSpace)
enum class AttributesSequence { Normalize, Preserve };
Q_ENUM(AttributesSequence)
+ // Always: always add a semicolon at the end of statement
+ // Essential: adds semicolon only when ASI would not insert for us
+ enum class SemicolonRule { Always, Essential };
+ Q_ENUM(SemicolonRule)
int maxLineLength = -1; // -1 means no limit
int minContentLength = 10;
@@ -97,6 +101,7 @@ public:
bool objectsSpacing = false;
bool functionsSpacing = false;
bool sortImports = false;
+ SemicolonRule semicolonRule = SemicolonRule::Always;
};
class LineWriter;
@@ -130,6 +135,7 @@ public:
LineWriter &ensureNewline(int nNewlines = 1, TextAddType t = TextAddType::Extra);
LineWriter &ensureSpace(TextAddType t = TextAddType::Extra);
LineWriter &ensureSpace(QStringView space, TextAddType t = TextAddType::Extra);
+ LineWriter &ensureSemicolon(TextAddType t = TextAddType::Extra);
LineWriter &newline()
{
diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp
index 9576e3c8cb..d58ab39ce5 100644
--- a/src/qmldom/qqmldomreformatter.cpp
+++ b/src/qmldom/qqmldomreformatter.cpp
@@ -10,9 +10,7 @@
#include <QtQml/private/qqmljslexer_p.h>
#include <QString>
-
#include <algorithm>
-#include <limits>
QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -46,6 +44,10 @@ void ScriptFormatter::lnAcceptIndented(Node *node)
bool ScriptFormatter::acceptBlockOrIndented(Node *ast, bool finishWithSpaceOrNewline)
{
+ if (auto *es = cast<EmptyStatement *>(ast)) {
+ writeOutSemicolon(es);
+ return false;
+ }
if (cast<Block *>(ast)) {
lw.lineWriter.ensureSpace();
accept(ast);
@@ -92,7 +94,7 @@ bool ScriptFormatter::visit(StringLiteral *ast)
// correctly handle multiline literals
if (ast->literalToken.length == 0)
return true;
- QStringView str = loc2Str(ast->literalToken);
+ QStringView str = m_script->loc2Str(ast->literalToken);
if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
out(str.mid(0, 1));
lw.indentNextlines = false;
@@ -288,7 +290,7 @@ bool ScriptFormatter::visit(TemplateLiteral *ast)
{
// correctly handle multiline literals
if (ast->literalToken.length != 0) {
- QStringView str = loc2Str(ast->literalToken);
+ QStringView str = m_script->loc2Str(ast->literalToken);
if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
out(str.mid(0, 1));
lw.indentNextlines = false;
@@ -474,7 +476,7 @@ bool ScriptFormatter::visit(VariableStatement *ast)
lw.lineWriter.ensureSpace();
accept(ast->declarations);
if (addSemicolons())
- out(";");
+ writeOutSemicolon(ast);
return false;
}
@@ -512,9 +514,9 @@ bool ScriptFormatter::visit(PatternElement *ast)
return false;
}
-bool ScriptFormatter::visit(EmptyStatement *ast)
+bool ScriptFormatter::visit(EmptyStatement *)
{
- out(ast->semicolonToken);
+ lw.lineWriter.ensureSemicolon();
return false;
}
@@ -575,14 +577,21 @@ bool ScriptFormatter::visit(ForStatement *ast)
out(pe->declarationKindToken);
lw.lineWriter.ensureSpace();
}
+ bool first = true;
for (VariableDeclarationList *it = ast->declarations; it; it = it->next) {
+ if (!std::exchange(first, false)) {
+ out(",");
+ lw.lineWriter.ensureSpace();
+ }
accept(it->declaration);
}
}
- out(";"); // ast->firstSemicolonToken
+ // We don't use writeOutSemicolon() here because we need a semicolon unconditionally.
+ // Repeats for the second semicolon token below.
+ out(u";"); // ast->firstSemicolonToken
lw.lineWriter.ensureSpace();
accept(ast->condition);
- out(";"); // ast->secondSemicolonToken
+ out(u";"); // ast->secondSemicolonToken
lw.lineWriter.ensureSpace();
accept(ast->expression);
out(ast->rparenToken);
@@ -617,7 +626,7 @@ bool ScriptFormatter::visit(ContinueStatement *ast)
out(ast->identifierToken);
}
if (addSemicolons())
- out(";");
+ writeOutSemicolon(ast);
return false;
}
@@ -629,7 +638,7 @@ bool ScriptFormatter::visit(BreakStatement *ast)
out(ast->identifierToken);
}
if (addSemicolons())
- out(";");
+ writeOutSemicolon(ast);
return false;
}
@@ -642,7 +651,7 @@ bool ScriptFormatter::visit(ReturnStatement *ast)
accept(ast->expression);
}
if (ast->returnToken.length > 0 && addSemicolons())
- out(";");
+ writeOutSemicolon(ast);
return false;
}
@@ -667,7 +676,7 @@ bool ScriptFormatter::visit(ThrowStatement *ast)
accept(ast->expression);
}
if (addSemicolons())
- out(";");
+ writeOutSemicolon(ast);
return false;
}
@@ -860,7 +869,7 @@ bool ScriptFormatter::visit(StatementList *ast)
for (StatementList *it = ast; it; it = it->next) {
// ### work around parser bug: skip empty statements with wrong tokens
if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(it->statement)) {
- if (loc2Str(emptyStatement->semicolonToken) != QLatin1String(";"))
+ if (m_script->loc2Str(emptyStatement->semicolonToken) != QLatin1String(";"))
continue;
}
@@ -946,7 +955,7 @@ bool ScriptFormatter::visit(Expression *el)
bool ScriptFormatter::visit(ExpressionStatement *el)
{
if (addSemicolons())
- postOps[el->expression].append([this]() { out(";"); });
+ postOps[el->expression].append([this, el]() { writeOutSemicolon(el); });
return true;
}
@@ -1107,16 +1116,16 @@ void ScriptFormatter::endVisit(ComputedPropertyName *)
void ScriptFormatter::endVisit(AST::ExportDeclaration *ast)
{
// add a semicolon at the end of the following expressions
- // export * FromClause ;
+ // export * FromClause
// export ExportClause FromClause ;
if (ast->fromClause) {
- out(";");
+ writeOutSemicolon(ast);
}
// add a semicolon at the end of the following expressions
// export ExportClause ;
if (ast->exportClause && !ast->fromClause) {
- out(";");
+ writeOutSemicolon(ast);
}
// add a semicolon at the end of the following expressions
@@ -1125,7 +1134,7 @@ void ScriptFormatter::endVisit(AST::ExportDeclaration *ast)
// lookahead ∉ { function, class }
if (!(ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration
|| ast->variableStatementOrDeclaration->kind == Node::Kind_ClassDeclaration)) {
- out(";");
+ writeOutSemicolon(ast);
}
// ArrowFunction in QQmlJS::AST is handled with the help of FunctionDeclaration
// and not as part of AssignmentExpression (as per ECMA
@@ -1133,7 +1142,7 @@ void ScriptFormatter::endVisit(AST::ExportDeclaration *ast)
if (ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration
&& static_cast<AST::FunctionDeclaration *>(ast->variableStatementOrDeclaration)
->isArrowFunction) {
- out(";");
+ writeOutSemicolon(ast);
}
}
}
@@ -1154,9 +1163,9 @@ void ScriptFormatter::endVisit(AST::NamedImports *ast)
out(ast->rightBraceToken);
}
-void ScriptFormatter::endVisit(AST::ImportDeclaration *)
+void ScriptFormatter::endVisit(AST::ImportDeclaration *id)
{
- out(";");
+ writeOutSemicolon(id);
}
void ScriptFormatter::throwRecursionDepthError()
@@ -1164,14 +1173,84 @@ void ScriptFormatter::throwRecursionDepthError()
out("/* ERROR: Hit recursion limit ScriptFormatter::visiting AST, rewrite failed */");
}
-void reformatAst(OutWriter &lw, const std::shared_ptr<AstComments> &comments,
- const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *n)
-{
- if (n) {
- ScriptFormatter formatter(lw, comments, loc2Str, n);
+// This is a set of characters that are not allowed to be at the beginning of a line
+// after a semicolon for ASI.
+using namespace Qt::StringLiterals;
+static constexpr QLatin1StringView restrictedChars = "([/+-"_L1;
+
+// Given an existing semicolon, can we safely remove it without changing behavior
+bool ScriptFormatter::canRemoveSemicolon(AST::Node *node)
+{
+ const auto canRelyOnASI = [this](Node *node) {
+ auto nodeLoc = node->lastSourceLocation().offset + 1;
+ auto code = m_script->engine()->code();
+ // Bounds check for nodeLoc
+ if (qsizetype(nodeLoc) >= code.size())
+ return false;
+ auto startIt = code.begin() + nodeLoc;
+ auto endIt = std::find_first_of(startIt, code.end(), restrictedChars.begin(),
+ restrictedChars.end());
+ // No restricted character found, then it is safe to remove the semicolon
+ if (endIt == code.end())
+ return true;
+
+ // Check if there is at least one character between nodeLoc and the found character
+ // that are neither space chars nor semicolons.
+ bool hasOtherChars =
+ std::any_of(startIt, endIt, [](QChar ch) { return !(ch.isSpace() || ch == u';'); });
+
+ if (hasOtherChars)
+ return true;
+
+ // Check if there is no linebreak between nodeLoc and the found character
+ return std::none_of(startIt, endIt, [](QChar c) { return c == u'\n'; });
+ };
+
+ // Check if the node is a statement that requires a semicolon to avoid ASI issues
+ switch (node->kind) {
+ case AST::Node::Kind_ExpressionStatement:
+ return canRelyOnASI(cast<ExpressionStatement *>(node));
+ case AST::Node::Kind_VariableStatement:
+ return canRelyOnASI(cast<VariableStatement *>(node));
+ case AST::Node::Kind_EmptyStatement:
+ return false;
+ case AST::Node::Kind_ContinueStatement:
+ case AST::Node::Kind_BreakStatement:
+ case AST::Node::Kind_ReturnStatement:
+ case AST::Node::Kind_ThrowStatement:
+ case AST::Node::Kind_ExportDeclaration:
+ case AST::Node::Kind_ImportDeclaration:
+ case AST::Node::Kind_FromClause:
+ case AST::Node::Kind_ExportClause:
+ default:
+ return true;
}
}
+OutWriter &ScriptFormatter::writeOutSemicolon(AST::Node *node)
+{
+ if (!node)
+ return lw;
+ switch (lw.lineWriter.options().semicolonRule) {
+ case LineWriterOptions::SemicolonRule::Essential:
+ if (!canRemoveSemicolon(node))
+ out(u";");
+ lw.lineWriter.ensureNewline();
+ return lw;
+ case LineWriterOptions::SemicolonRule::Always:
+ out(u";");
+ return lw;
+ default:
+ Q_UNREACHABLE_RETURN(lw);
+ }
+}
+
+void reformatAst(OutWriter &lw, const QQmlJS::Dom::ScriptExpression *const script)
+{
+ if (script)
+ ScriptFormatter formatter(lw, script);
+}
+
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomreformatter_p.h b/src/qmldom/qqmldomreformatter_p.h
index ae6b27a43e..a13425dec7 100644
--- a/src/qmldom/qqmldomreformatter_p.h
+++ b/src/qmldom/qqmldomreformatter_p.h
@@ -20,6 +20,7 @@
#include "qqmldomoutwriter_p.h"
#include "qqmldom_fwd_p.h"
#include "qqmldomcomments_p.h"
+#include "qqmldomelements_p.h"
#include <QtQml/private/qqmljsast_p.h>
@@ -31,11 +32,12 @@ class ScriptFormatter final : protected AST::JSVisitor
{
public:
// TODO QTBUG-121988
- ScriptFormatter(OutWriter &lw, const std::shared_ptr<AstComments> &comments,
- const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *node)
- : lw(lw), comments(comments), loc2Str(loc2Str)
+ ScriptFormatter(OutWriter &lw, const ScriptExpression *const script) : lw(lw), m_script(script)
{
- accept(node);
+ if (m_script) {
+ comments = m_script->astComments();
+ accept(m_script->ast());
+ }
}
protected:
@@ -44,7 +46,7 @@ protected:
inline void out(const SourceLocation &loc)
{
if (loc.length != 0)
- out(loc2Str(loc));
+ out(m_script->loc2Str(loc));
}
enum CommentOption { NoSpace, SpaceBeforePostComment, OnlyComments };
void outWithComments(const SourceLocation &loc, AST::Node *node, CommentOption option = NoSpace)
@@ -218,17 +220,16 @@ protected:
private:
bool addSemicolons() const { return expressionDepth > 0; }
-
+ bool canRemoveSemicolon(AST::Node *node);
+ OutWriter &writeOutSemicolon(AST::Node *);
OutWriter &lw;
std::shared_ptr<AstComments> comments;
- std::function<QStringView(SourceLocation)> loc2Str;
+ const ScriptExpression *const m_script = nullptr; // outlives this
QHash<AST::Node *, QList<std::function<void()>>> postOps;
int expressionDepth = 0;
};
-QMLDOM_EXPORT void reformatAst(
- OutWriter &lw, const std::shared_ptr<AstComments> &comments,
- const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *n);
+QMLDOM_EXPORT void reformatAst(OutWriter &lw, const QQmlJS::Dom::ScriptExpression *const script);
} // namespace Dom
} // namespace QQmlJS
diff --git a/src/qmlformat/qqmlformatoptions.cpp b/src/qmlformat/qqmlformatoptions.cpp
index e76fffcb0d..d3b1686061 100644
--- a/src/qmlformat/qqmlformatoptions.cpp
+++ b/src/qmlformat/qqmlformatoptions.cpp
@@ -180,6 +180,13 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
QStringLiteral("Sort imports alphabetically "
"(Warning: this might change semantics if a given "
"name identifies types in multiple modules!).")));
+ QCommandLineOption semicolonRuleOption(
+ QStringList() << "semicolon-rule"_L1,
+ QStringLiteral("Specify the semicolon rule to use (always, essential).\n"
+ "always: always adds semicolon [default].\n"
+ "essential: adds only when ASI wouldn't be relied on."),
+ "rule"_L1, "always"_L1);
+ parser.addOption(semicolonRuleOption);
parser.addPositionalArgument("filenames"_L1, "files to be processed by qmlformat"_L1);
@@ -277,6 +284,19 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
options.mark(Settings::NewlineType);
options.setNewline(QQmlFormatOptions::parseEndings(parser.value("newline"_L1)));
}
+
+ if (parser.isSet(semicolonRuleOption)) {
+ options.mark(Settings::SemicolonRule);
+ const auto value = parser.value(semicolonRuleOption);
+ if (value == "always"_L1) {
+ options.setSemicolonRule(QQmlJS::Dom::LineWriterOptions::SemicolonRule::Always);
+ } else if (value == "essential"_L1) {
+ options.setSemicolonRule(QQmlJS::Dom::LineWriterOptions::SemicolonRule::Essential);
+ } else {
+ options.addError("Error: Invalid value passed to --semicolon-rule."_L1);
+ return options;
+ }
+ }
options.setFiles(files);
options.setArguments(parser.positionalArguments());
diff --git a/src/qmlformat/qqmlformatoptions_p.h b/src/qmlformat/qqmlformatoptions_p.h
index 63ba509b88..011ad4f9fa 100644
--- a/src/qmlformat/qqmlformatoptions_p.h
+++ b/src/qmlformat/qqmlformatoptions_p.h
@@ -83,6 +83,16 @@ public:
void setMaxColumnWidth(int width) { m_options.maxLineLength = width; }
bool isMaxColumnWidthSet() const { return m_options.maxLineLength > 0; }
+ void setSemicolonRule(QQmlJS::Dom::LineWriterOptions::SemicolonRule rule)
+ {
+ m_options.semicolonRule = rule;
+ }
+
+ QQmlJS::Dom::LineWriterOptions::SemicolonRule semicolonRule() const
+ {
+ return m_options.semicolonRule;
+ }
+
QQmlJS::Dom::LineWriterOptions optionsForCode(const QString &code) const
{
QQmlJS::Dom::LineWriterOptions result = m_options;
@@ -134,6 +144,7 @@ public:
ObjectsSpacing,
FunctionsSpacing,
SortImports,
+ SemicolonRule,
SettingsCount
};
diff --git a/src/qmlformat/qqmlformatsettings.cpp b/src/qmlformat/qqmlformatsettings.cpp
index 8ee0f44f77..181a3d9514 100644
--- a/src/qmlformat/qqmlformatsettings.cpp
+++ b/src/qmlformat/qqmlformatsettings.cpp
@@ -13,4 +13,5 @@ QQmlFormatSettings::QQmlFormatSettings(const QString &toolName) : QQmlToolingSet
addOption(s_objectsSpacingSetting, false);
addOption(s_functionsSpacingSetting, false);
addOption(s_sortImportsSetting, false);
+ addOption(s_semiColonRuleSetting, QStringLiteral("always"));
}
diff --git a/src/qmlformat/qqmlformatsettings_p.h b/src/qmlformat/qqmlformatsettings_p.h
index d62b733e35..9630439858 100644
--- a/src/qmlformat/qqmlformatsettings_p.h
+++ b/src/qmlformat/qqmlformatsettings_p.h
@@ -31,6 +31,7 @@ public:
static const inline QLatin1StringView s_objectsSpacingSetting = QLatin1String("ObjectsSpacing");
static const inline QLatin1StringView s_functionsSpacingSetting = QLatin1String("FunctionsSpacing");
static const inline QLatin1StringView s_sortImportsSetting = QLatin1String("SortImports");
+ static const inline QLatin1StringView s_semiColonRuleSetting = QLatin1String("SemicolonRule");
};
QT_END_NAMESPACE
diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp
index d0c9ac9201..898bfd0334 100644
--- a/src/qmlls/qqmllsutils.cpp
+++ b/src/qmlls/qqmllsutils.cpp
@@ -1362,15 +1362,16 @@ static std::optional<ExpressionType> resolveFieldMemberExpressionType(const DomI
// Enumerations should live under the root element scope of the file that defines the enum,
// therefore use the DomItem to find the root element of the qml file instead of directly
// using owner->semanticScope.
- const auto scope = item.goToFile(owner->semanticScope->filePath())
- .rootQmlObject(GoTo::MostLikely)
- .semanticScope();
- if (scope->hasEnumerationKey(name)) {
- return ExpressionType{ name, scope, EnumeratorValueIdentifier };
- }
- // Or it is a enum name <TypeName>.<EnumName>.<EnumValue>
- else if (scope->hasEnumeration(name)) {
- return ExpressionType{ name, scope, EnumeratorIdentifier };
+ if (const auto scope = item.goToFile(owner->semanticScope->filePath())
+ .rootQmlObject(GoTo::MostLikely)
+ .semanticScope()) {
+ if (scope->hasEnumerationKey(name)) {
+ return ExpressionType{ name, scope, EnumeratorValueIdentifier };
+ }
+ // Or it is a enum name <TypeName>.<EnumName>.<EnumValue>
+ else if (scope->hasEnumeration(name)) {
+ return ExpressionType{ name, scope, EnumeratorIdentifier };
+ }
}
// check inline components <TypeName>.<InlineComponentName>
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 9aca664ec6..459d348b9b 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -43,7 +43,7 @@ public:
};
-class Q_AUTOTEST_EXPORT QQuickItemViewChangeSet
+class Q_QUICK_EXPORT QQuickItemViewChangeSet
{
public:
QQuickItemViewChangeSet();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 25397216bb..3753832a98 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -71,6 +71,8 @@ QSurfaceFormat QSGSoftwareContext::defaultSurfaceFormat() const
format.setRenderableType(QSurfaceFormat::DefaultRenderableType);
format.setMajorVersion(0);
format.setMinorVersion(0);
+ if (QQuickWindow::hasDefaultAlphaBuffer())
+ format.setAlphaBufferSize(8);
return format;
}
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 3c8da0fc6a..319c98ef93 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -580,14 +580,6 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!ensureRhi(window, data))
return;
- bool lastDirtyWindow = true;
- for (auto it = m_windows.cbegin(), end = m_windows.cend(); it != end; ++it) {
- if (it->updatePending) {
- lastDirtyWindow = false;
- break;
- }
- }
-
cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
// Event delivery/processing triggered the window to be deleted or stop rendering.
if (!m_windows.contains(window))
@@ -689,8 +681,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
data.rhi->makeThreadLocalNativeContextCurrent();
cd->syncSceneGraph();
- if (lastDirtyWindow)
- data.rc->endSync();
+ data.rc->endSync();
if (profileFrames)
syncTime = renderTimer.nsecsElapsed();
@@ -824,16 +815,12 @@ void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window)
if (winDataIt == m_windows.end())
return;
- // Even if the window is not renderable,
- // renderWindow() called on different window
- // should not delete QSGTexture's
- // from this unrenderable window.
- winDataIt->updatePending = true;
-
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
if (!cd->isRenderable())
return;
+ winDataIt->updatePending = true;
+
// An updatePolish() implementation may call update() to get the QQuickItem
// dirtied. That's fine but it also leads to calling this function.
// Requesting another update is a waste then since the updatePolish() call
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 9157e2edd2..bd7414492c 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -105,6 +105,9 @@ WM_Obscure = QEvent::User + 1,
// (updatePaintNode())
WM_RequestSync = QEvent::User + 2,
+// Passed from the RL to the RT when a window is exposed
+WM_Exposed = QEvent::User + 3,
+
// Passed by the RL to the RT to free up maybe release SG and GL contexts
// if no windows are rendering.
WM_TryRelease = QEvent::User + 4,
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index e45ef8768b..6a0286dbce 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -338,6 +338,17 @@ bool QSGRenderThread::event(QEvent *e)
return true; }
+
+ case WM_Exposed: {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_Exposed");
+
+ mutex.lock();
+ window = static_cast<WMWindowEvent *>(e)->window;
+ waitCondition.wakeOne();
+ mutex.unlock();
+
+ return true; }
+
case WM_RequestSync: {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestSync");
WMSyncEvent *se = static_cast<WMSyncEvent *>(e);
@@ -641,8 +652,14 @@ void QSGRenderThread::syncAndRender()
// An update request could still be delivered right before we get an
// unexpose. With Vulkan on Windows for example attempting to render
// leads to failures at this stage since the surface size is already 0.
- if (effectiveOutputSize.isEmpty())
+ if (effectiveOutputSize.isEmpty()) {
+ if (syncRequested) {
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
return;
+ }
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
@@ -1286,10 +1303,6 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
}
}
- // set this early as we'll be rendering shortly anyway and this avoids
- // specialcasing exposure in polishAndSync.
- w->thread->window = window;
-
#ifndef QT_NO_DEBUG
if (w->window->width() <= 0 || w->window->height() <= 0
|| (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
@@ -1308,6 +1321,10 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
if (!w->thread->isRunning()) {
qCDebug(QSG_LOG_RENDERLOOP, "- starting render thread");
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // specialcasing exposure in polishAndSync.
+ w->thread->window = window;
+
if (!w->thread->rhi) {
QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
if (!w->thread->offscreenSurface)
@@ -1332,6 +1349,12 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
} else {
qCDebug(QSG_LOG_RENDERLOOP, "- render thread already running");
+
+ // set w->thread->window here too, but using an event so it's thread-safe
+ w->thread->mutex.lock();
+ w->thread->postEvent(new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
}
polishAndSync(w, true);
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
index 7b49fb7ef3..3e90e2f041 100644
--- a/src/quicktemplates/qquickmenu.cpp
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -36,6 +36,7 @@
#include <private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickrendercontrol_p.h>
@@ -979,6 +980,22 @@ bool QQuickMenuPrivate::prepareEnterTransition()
// the right, it flips on the other side of the parent menu.
allowHorizontalFlip = cascade && parentMenu;
+ // Enter transitions may want to animate the Menu's height based on its implicitHeight.
+ // The Menu's implicitHeight is typically based on the ListView's contentHeight,
+ // among other things. The docs for ListView's forceLayout function say:
+ // "Responding to changes in the model is usually batched to happen only once per frame."
+ // As e.g. NumberAnimation's from and to values are set before any polishes happen,
+ // any re-evaluation of their bindings happen too late, and the starting height can be
+ // out-dated when menu items are added after component completion
+ // (QQuickItemView::componentComplete does a layout, so items declared as children aren't
+ // affected by this). To account for this, we force a layout before the transition starts.
+ // We try to avoid unnecessary re-layouting if we can avoid it.
+ auto *contentItemAsListView = qobject_cast<QQuickListView *>(contentItem);
+ if (contentItemAsListView) {
+ if (QQuickItemViewPrivate::get(contentItemAsListView)->currentChanges.hasPendingChanges())
+ contentItemAsListView->forceLayout();
+ }
+
if (!QQuickPopupPrivate::prepareEnterTransition())
return false;
diff --git a/src/quickvectorimage/generator/qquicknodeinfo_p.h b/src/quickvectorimage/generator/qquicknodeinfo_p.h
index 44226d6a78..fea49db107 100644
--- a/src/quickvectorimage/generator/qquicknodeinfo_p.h
+++ b/src/quickvectorimage/generator/qquicknodeinfo_p.h
@@ -70,6 +70,14 @@ struct StrokeStyle
}
};
+struct PathTrimInfo
+{
+ bool enabled = false;
+ QQuickAnimatedProperty start = QQuickAnimatedProperty(QVariant::fromValue(0.0));
+ QQuickAnimatedProperty end = QQuickAnimatedProperty(QVariant::fromValue(1.0));
+ QQuickAnimatedProperty offset = QQuickAnimatedProperty(QVariant::fromValue(0.0));
+};
+
struct PathNodeInfo : NodeInfo
{
QPainterPath painterPath;
@@ -79,6 +87,7 @@ struct PathNodeInfo : NodeInfo
StrokeStyle strokeStyle;
QGradient grad;
QTransform fillTransform;
+ PathTrimInfo trim;
};
struct TextNodeInfo : NodeInfo
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator.cpp b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
index 72b6b51035..2e71b415bd 100644
--- a/src/quickvectorimage/generator/qquickqmlgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
@@ -467,6 +467,13 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
generateTransform(xf);
}
+ if (info.trim.enabled) {
+ stream() << "trim.start: " << info.trim.start.defaultValue().toReal();
+ stream() << "trim.end: " << info.trim.end.defaultValue().toReal();
+ stream() << "trim.offset: " << info.trim.offset.defaultValue().toReal();
+
+ }
+
if (fillRule == QQuickShapePath::WindingFill)
stream() << "fillRule: ShapePath.WindingFill";
else
@@ -484,6 +491,12 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
m_indentLevel--;
stream() << "}";
+ if (info.trim.enabled) {
+ generatePropertyAnimation(info.trim.start, shapePathId + QStringLiteral(".trim"), QStringLiteral("start"));
+ generatePropertyAnimation(info.trim.end, shapePathId + QStringLiteral(".trim"), QStringLiteral("end"));
+ generatePropertyAnimation(info.trim.offset, shapePathId + QStringLiteral(".trim"), QStringLiteral("offset"));
+ }
+
generatePropertyAnimation(info.strokeStyle.color, shapePathId, QStringLiteral("strokeColor"));
generatePropertyAnimation(info.strokeStyle.opacity, shapePathId, QStringLiteral("strokeColor"), AnimationType::ColorOpacity);
generatePropertyAnimation(info.fillColor, shapePathId, QStringLiteral("fillColor"));