QML: Insert aliases in inline components when loading from disk cache
So far we did not completely restore aliases in inline components. This
was masked by the fact that until recently we failed to load inline
components from the disk cache and always loaded them from source
instead.
To fix this, refactor QQmlComponentAndAliasResolver to work for both,
QmlIR and QV4::CompiledData. With QmlIR, it populates the relevant data
structures. With QV4::CompiledData, it sanity-checks them. The
sanity-checks do incur some overhead, but given recent events, we
should err on the side of caution here.
Since QQmlComponentAndAliasResolver has received all the fixes we've
applied to make inline components work, this should lead to inline
components loaded from cache files to work the same way as those
compiled from source.
In turn, we can drop some methods of QQmlPropertyCacheAliasCreator.
Amends commit 131db085a7
Pick-to: 6.5
Fixes: QTBUG-111766
Fixes: QTBUG-111857
Change-Id: I9cc75e700a5fe5810a866e9aa930b9811368b1b4
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
4c6d0b2bf0
commit
2d7fe23b41
|
@ -267,6 +267,7 @@ qt_internal_add_qml_module(Qml
|
|||
qml/qqmlboundsignal.cpp qml/qqmlboundsignal_p.h
|
||||
qml/qqmlbuiltinfunctions.cpp qml/qqmlbuiltinfunctions_p.h
|
||||
qml/qqmlcomponent.cpp qml/qqmlcomponent.h qml/qqmlcomponent_p.h
|
||||
qml/qqmlcomponentandaliasresolver_p.h
|
||||
qml/qqmlcomponentattached_p.h
|
||||
qml/qqmlcontext.cpp qml/qqmlcontext.h qml/qqmlcontext_p.h
|
||||
qml/qqmlcontextdata.cpp qml/qqmlcontextdata_p.h
|
||||
|
|
|
@ -1080,6 +1080,7 @@ public:
|
|||
|
||||
const Binding *bindingsBegin() const { return bindingTable(); }
|
||||
const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
|
||||
int bindingCount() const { return nBindings; }
|
||||
|
||||
const Property *propertiesBegin() const { return propertyTable(); }
|
||||
const Property *propertiesEnd() const { return propertyTable() + nProperties; }
|
||||
|
|
|
@ -167,6 +167,9 @@ struct PoolList
|
|||
bool operator!=(const Iterator &rhs) const {
|
||||
return ptr != rhs.ptr;
|
||||
}
|
||||
|
||||
operator T *() { return ptr; }
|
||||
operator const T *() const { return ptr; }
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(first); }
|
||||
|
|
|
@ -154,9 +154,22 @@ public:
|
|||
std::unique_ptr<CompilationUnitMapper> backingFile;
|
||||
|
||||
// --- interface for QQmlPropertyCacheCreator
|
||||
using CompiledObject = CompiledData::Object;
|
||||
using CompiledFunction = CompiledData::Function;
|
||||
using CompiledObject = const CompiledData::Object;
|
||||
using CompiledFunction = const CompiledData::Function;
|
||||
using CompiledBinding = const CompiledData::Binding;
|
||||
enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault };
|
||||
|
||||
// Empty dummy. We don't need to do this when loading from cache.
|
||||
class IdToObjectMap
|
||||
{
|
||||
public:
|
||||
void insert(int, int) {}
|
||||
void clear() {}
|
||||
|
||||
// We have already checked uniqueness of IDs when creating the CU
|
||||
bool contains(int) { return false; }
|
||||
};
|
||||
|
||||
ListPropertyAssignBehavior listPropertyAssignBehavior() const
|
||||
{
|
||||
if (data->flags & CompiledData::Unit::ListPropertyAssignReplace)
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
#ifndef QQMLCOMPONENTANDALIASRESOLVER_P_H
|
||||
#define QQMLCOMPONENTANDALIASRESOLVER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtQml/qqmlcomponent.h>
|
||||
#include <QtQml/qqmlerror.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qhash.h>
|
||||
|
||||
#include <private/qqmltypeloader_p.h>
|
||||
#include <private/qqmlpropertycachecreator_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcQmlTypeCompiler);
|
||||
|
||||
template<typename ObjectContainer>
|
||||
class QQmlComponentAndAliasResolver
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QQmlComponentAndAliasResolver)
|
||||
public:
|
||||
using CompiledObject = typename ObjectContainer::CompiledObject;
|
||||
using CompiledBinding = typename ObjectContainer::CompiledBinding;
|
||||
|
||||
QQmlComponentAndAliasResolver(
|
||||
ObjectContainer *compiler,
|
||||
QQmlEnginePrivate *enginePrivate,
|
||||
QQmlPropertyCacheVector *propertyCaches);
|
||||
|
||||
[[nodiscard]] QQmlError resolve(int root = 0);
|
||||
|
||||
private:
|
||||
enum AliasResolutionResult {
|
||||
NoAliasResolved,
|
||||
SomeAliasesResolved,
|
||||
AllAliasesResolved
|
||||
};
|
||||
|
||||
// To be specialized for each container
|
||||
void allocateNamedObjects(CompiledObject *object) const;
|
||||
void setObjectId(int index) const;
|
||||
[[nodiscard]] bool markAsComponent(int index) const;
|
||||
[[nodiscard]] AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
|
||||
[[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
|
||||
|
||||
[[nodiscard]] QQmlError findAndRegisterImplicitComponents(
|
||||
const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
|
||||
[[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
|
||||
[[nodiscard]] QQmlError resolveAliases(int componentIndex);
|
||||
|
||||
QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
|
||||
QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
|
||||
|
||||
[[nodiscard]] QQmlError error(
|
||||
const QV4::CompiledData::Location &location,
|
||||
const QString &description)
|
||||
{
|
||||
QQmlError error;
|
||||
error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
|
||||
error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
|
||||
error.setDescription(description);
|
||||
error.setUrl(m_compiler->url());
|
||||
return error;
|
||||
}
|
||||
|
||||
template<typename Token>
|
||||
[[nodiscard]] QQmlError error(Token token, const QString &description)
|
||||
{
|
||||
return error(token->location, description);
|
||||
}
|
||||
|
||||
static bool isUsableComponent(const QMetaObject *metaObject)
|
||||
{
|
||||
// The metaObject is a component we're interested in if it either is a QQmlComponent itself
|
||||
// or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
|
||||
// qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
|
||||
|
||||
if (metaObject == &QQmlComponent::staticMetaObject)
|
||||
return true;
|
||||
|
||||
for (; metaObject; metaObject = metaObject->superClass()) {
|
||||
if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectContainer *m_compiler = nullptr;
|
||||
QQmlEnginePrivate *m_enginePrivate = nullptr;
|
||||
|
||||
// Implicit component insertion may have added objects and thus we also need
|
||||
// to extend the symmetric propertyCaches. Therefore, non-const propertyCaches.
|
||||
QQmlPropertyCacheVector *m_propertyCaches = nullptr;
|
||||
|
||||
// indices of the objects that are actually Component {}
|
||||
QVector<quint32> m_componentRoots;
|
||||
QVector<int> m_objectsWithAliases;
|
||||
typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
|
||||
};
|
||||
|
||||
template<typename ObjectContainer>
|
||||
QQmlComponentAndAliasResolver<ObjectContainer>::QQmlComponentAndAliasResolver(
|
||||
ObjectContainer *compiler,
|
||||
QQmlEnginePrivate *enginePrivate,
|
||||
QQmlPropertyCacheVector *propertyCaches)
|
||||
: m_compiler(compiler)
|
||||
, m_enginePrivate(enginePrivate)
|
||||
, m_propertyCaches(propertyCaches)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename ObjectContainer>
|
||||
QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::findAndRegisterImplicitComponents(
|
||||
const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
|
||||
{
|
||||
QQmlPropertyResolver propertyResolver(propertyCache);
|
||||
|
||||
const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1
|
||||
? propertyCache->parent()->defaultProperty()
|
||||
: propertyCache->defaultProperty();
|
||||
|
||||
for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
|
||||
if (binding->type() != QV4::CompiledData::Binding::Type_Object)
|
||||
continue;
|
||||
if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
|
||||
continue;
|
||||
|
||||
auto targetObject = m_compiler->objectAt(binding->value.objectIndex);
|
||||
auto typeReference = resolvedType(targetObject->inheritedTypeNameIndex);
|
||||
Q_ASSERT(typeReference);
|
||||
|
||||
const QMetaObject *firstMetaObject = nullptr;
|
||||
const auto type = typeReference->type();
|
||||
if (type.isValid())
|
||||
firstMetaObject = type.metaObject();
|
||||
else if (const auto compilationUnit = typeReference->compilationUnit())
|
||||
firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
|
||||
if (isUsableComponent(firstMetaObject))
|
||||
continue;
|
||||
|
||||
// if here, not a QQmlComponent, so needs wrapping
|
||||
const QQmlPropertyData *pd = nullptr;
|
||||
if (binding->propertyNameIndex != quint32(0)) {
|
||||
bool notInRevision = false;
|
||||
pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision);
|
||||
} else {
|
||||
pd = defaultProperty;
|
||||
}
|
||||
if (!pd || !pd->isQObject())
|
||||
continue;
|
||||
|
||||
// If the version is given, use it and look up by QQmlType.
|
||||
// Otherwise, make sure we look up by metaobject.
|
||||
// TODO: Is this correct?
|
||||
QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
|
||||
? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
|
||||
: QQmlMetaType::rawPropertyCacheForType(pd->propType());
|
||||
const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
|
||||
while (mo) {
|
||||
if (mo == &QQmlComponent::staticMetaObject)
|
||||
break;
|
||||
mo = mo->superClass();
|
||||
}
|
||||
|
||||
if (!mo)
|
||||
continue;
|
||||
|
||||
if (!wrapImplicitComponent(binding))
|
||||
return error(binding, tr("Cannot wrap implicit component"));
|
||||
}
|
||||
|
||||
return QQmlError();
|
||||
}
|
||||
|
||||
// Resolve ignores everything relating to inline components, except for implicit components.
|
||||
template<typename ObjectContainer>
|
||||
QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
|
||||
{
|
||||
// Detect real Component {} objects as well as implicitly defined components, such as
|
||||
// someItemDelegate: Item {}
|
||||
// In the implicit case Item is surrounded by a synthetic Component {} because the property
|
||||
// on the left hand side is of QQmlComponent type.
|
||||
const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
|
||||
|
||||
// root+1, as ic root is handled at the end
|
||||
const int startObjectIndex = root == 0 ? root : root+1;
|
||||
|
||||
for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
|
||||
auto obj = m_compiler->objectAt(i);
|
||||
const bool isInlineComponentRoot
|
||||
= obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
|
||||
const bool isPartOfInlineComponent
|
||||
= obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
|
||||
QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
|
||||
|
||||
bool isExplicitComponent = false;
|
||||
if (obj->inheritedTypeNameIndex) {
|
||||
auto *tref = resolvedType(obj->inheritedTypeNameIndex);
|
||||
Q_ASSERT(tref);
|
||||
if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
|
||||
isExplicitComponent = true;
|
||||
}
|
||||
|
||||
if (isInlineComponentRoot && isExplicitComponent) {
|
||||
qCWarning(lcQmlTypeCompiler).nospace().noquote()
|
||||
<< m_compiler->url().toString() << ":" << obj->location.line() << ":"
|
||||
<< obj->location.column()
|
||||
<< ": Using a Component as the root of an inline component is deprecated: "
|
||||
"inline components are "
|
||||
"automatically wrapped into Components when needed.";
|
||||
}
|
||||
|
||||
if (root == 0) {
|
||||
// normal component root, skip over anything inline component related
|
||||
if (isInlineComponentRoot || isPartOfInlineComponent)
|
||||
continue;
|
||||
} else if (!isPartOfInlineComponent || isInlineComponentRoot) {
|
||||
// We've left the current inline component (potentially entered a new one),
|
||||
// but we still need to resolve implicit components which are part of inline components.
|
||||
if (cache && !isExplicitComponent) {
|
||||
const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
|
||||
if (error.isValid())
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj->inheritedTypeNameIndex == 0 && !cache)
|
||||
continue;
|
||||
|
||||
if (!isExplicitComponent) {
|
||||
if (cache) {
|
||||
const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
|
||||
if (error.isValid())
|
||||
return error;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!markAsComponent(i))
|
||||
return error(obj, tr("Cannot mark object as component"));
|
||||
|
||||
// check if this object is the root
|
||||
if (i == 0) {
|
||||
if (isExplicitComponent)
|
||||
qCWarning(lcQmlTypeCompiler).nospace().noquote()
|
||||
<< m_compiler->url().toString() << ":" << obj->location.line() << ":"
|
||||
<< obj->location.column()
|
||||
<< ": Using a Component as the root of a qmldocument is deprecated: types "
|
||||
"defined in qml documents are "
|
||||
"automatically wrapped into Components when needed.";
|
||||
}
|
||||
|
||||
if (obj->functionCount() > 0)
|
||||
return error(obj, tr("Component objects cannot declare new functions."));
|
||||
if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
|
||||
return error(obj, tr("Component objects cannot declare new properties."));
|
||||
if (obj->signalCount() > 0)
|
||||
return error(obj, tr("Component objects cannot declare new signals."));
|
||||
|
||||
if (obj->bindingCount() == 0)
|
||||
return error(obj, tr("Cannot create empty component specification"));
|
||||
|
||||
const auto rootBinding = obj->bindingsBegin();
|
||||
const auto bindingsEnd = obj->bindingsEnd();
|
||||
|
||||
// Produce the more specific "no properties" error rather than the "invalid body" error
|
||||
// where possible.
|
||||
for (auto b = rootBinding; b != bindingsEnd; ++b) {
|
||||
if (b->propertyNameIndex == 0)
|
||||
continue;
|
||||
|
||||
return error(b, tr("Component elements may not contain properties other than id"));
|
||||
}
|
||||
|
||||
if (auto b = rootBinding;
|
||||
b->type() != QV4::CompiledData::Binding::Type_Object || ++b != bindingsEnd) {
|
||||
return error(obj, tr("Invalid component body specification"));
|
||||
}
|
||||
|
||||
// For the root object, we are going to collect ids/aliases and resolve them for as a
|
||||
// separate last pass.
|
||||
if (i != 0)
|
||||
m_componentRoots.append(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_componentRoots.size(); ++i) {
|
||||
CompiledObject *component = m_compiler->objectAt(m_componentRoots.at(i));
|
||||
const auto rootBinding = component->bindingsBegin();
|
||||
|
||||
m_idToObjectIndex.clear();
|
||||
m_objectsWithAliases.clear();
|
||||
|
||||
if (const QQmlError error = collectIdsAndAliases(rootBinding->value.objectIndex);
|
||||
error.isValid()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
allocateNamedObjects(component);
|
||||
|
||||
if (const QQmlError error = resolveAliases(m_componentRoots.at(i)); error.isValid())
|
||||
return error;
|
||||
}
|
||||
|
||||
// Collect ids and aliases for root
|
||||
m_idToObjectIndex.clear();
|
||||
m_objectsWithAliases.clear();
|
||||
|
||||
if (const QQmlError error = collectIdsAndAliases(root); error.isValid())
|
||||
return error;
|
||||
|
||||
allocateNamedObjects(m_compiler->objectAt(root));
|
||||
return resolveAliases(root);
|
||||
}
|
||||
|
||||
template<typename ObjectContainer>
|
||||
QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::collectIdsAndAliases(int objectIndex)
|
||||
{
|
||||
auto obj = m_compiler->objectAt(objectIndex);
|
||||
|
||||
if (obj->idNameIndex != 0) {
|
||||
if (m_idToObjectIndex.contains(obj->idNameIndex))
|
||||
return error(obj->locationOfIdProperty, tr("id is not unique"));
|
||||
setObjectId(objectIndex);
|
||||
m_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
|
||||
}
|
||||
|
||||
if (obj->aliasCount() > 0)
|
||||
m_objectsWithAliases.append(objectIndex);
|
||||
|
||||
// Stop at Component boundary
|
||||
if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
|
||||
return QQmlError();
|
||||
|
||||
for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd();
|
||||
binding != end; ++binding) {
|
||||
switch (binding->type()) {
|
||||
case QV4::CompiledData::Binding::Type_Object:
|
||||
case QV4::CompiledData::Binding::Type_AttachedProperty:
|
||||
case QV4::CompiledData::Binding::Type_GroupProperty:
|
||||
if (const QQmlError error = collectIdsAndAliases(binding->value.objectIndex);
|
||||
error.isValid()) {
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QQmlError();
|
||||
}
|
||||
|
||||
template<typename ObjectContainer>
|
||||
QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int componentIndex)
|
||||
{
|
||||
if (m_objectsWithAliases.isEmpty())
|
||||
return QQmlError();
|
||||
|
||||
QQmlPropertyCacheAliasCreator<ObjectContainer> aliasCacheCreator(m_propertyCaches, m_compiler);
|
||||
|
||||
bool atLeastOneAliasResolved;
|
||||
do {
|
||||
atLeastOneAliasResolved = false;
|
||||
QVector<int> pendingObjects;
|
||||
|
||||
for (int objectIndex: std::as_const(m_objectsWithAliases)) {
|
||||
|
||||
QQmlError error;
|
||||
const auto result = resolveAliasesInObject(objectIndex, &error);
|
||||
if (error.isValid())
|
||||
return error;
|
||||
|
||||
if (result == AllAliasesResolved) {
|
||||
QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
|
||||
*m_compiler->objectAt(componentIndex), objectIndex, m_enginePrivate);
|
||||
if (error.isValid())
|
||||
return error;
|
||||
atLeastOneAliasResolved = true;
|
||||
} else if (result == SomeAliasesResolved) {
|
||||
atLeastOneAliasResolved = true;
|
||||
pendingObjects.append(objectIndex);
|
||||
} else {
|
||||
pendingObjects.append(objectIndex);
|
||||
}
|
||||
}
|
||||
qSwap(m_objectsWithAliases, pendingObjects);
|
||||
} while (!m_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
|
||||
|
||||
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))
|
||||
return error(alias->location, tr("Circular alias reference detected"));
|
||||
}
|
||||
}
|
||||
|
||||
return QQmlError();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QQMLCOMPONENTANDALIASRESOLVER_P_H
|
|
@ -232,7 +232,7 @@ private:
|
|||
friend class QQmlCompiler;
|
||||
template <typename T> friend class QQmlPropertyCacheCreator;
|
||||
template <typename T> friend class QQmlPropertyCacheAliasCreator;
|
||||
friend class QQmlComponentAndAliasResolver;
|
||||
template <typename T> friend class QQmlComponentAndAliasResolver;
|
||||
friend class QQmlMetaObject;
|
||||
|
||||
QQmlPropertyCache(const QQmlMetaObjectPointer &metaObject) : _metaObject(metaObject) {}
|
||||
|
|
|
@ -115,14 +115,6 @@ public:
|
|||
*/
|
||||
QQmlError verifyNoICCycle();
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Fills the property caches for the CompiledObjects by
|
||||
calling buildMetaObjectsIncrementally until it can no
|
||||
longer resume.
|
||||
*/
|
||||
QQmlError buildMetaObjects();
|
||||
|
||||
enum class VMEMetaObjectIsRequired {
|
||||
Maybe,
|
||||
Always
|
||||
|
@ -232,20 +224,6 @@ QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectsIncrementally()
|
|||
return {diag, false, 0};
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline QQmlError
|
||||
QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
|
||||
{
|
||||
QQmlError error = verifyNoICCycle();
|
||||
if (error.isValid())
|
||||
return error;
|
||||
QQmlPropertyCacheCreatorBase::IncrementalResult result;
|
||||
do {
|
||||
result = buildMetaObjectsIncrementally();
|
||||
} while (result.canResume);
|
||||
return result.error;
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
|
||||
{
|
||||
|
@ -753,18 +731,16 @@ class QQmlPropertyCacheAliasCreator
|
|||
public:
|
||||
typedef typename ObjectContainer::CompiledObject CompiledObject;
|
||||
|
||||
QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
|
||||
|
||||
void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
|
||||
|
||||
QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
|
||||
QQmlPropertyCacheAliasCreator(
|
||||
QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
|
||||
QQmlError appendAliasesToPropertyCache(
|
||||
const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
|
||||
|
||||
private:
|
||||
void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
|
||||
QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type, QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
|
||||
|
||||
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
|
||||
|
||||
QQmlError propertyDataForAlias(
|
||||
const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
|
||||
QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
|
||||
QQmlEnginePrivate *enginePriv);
|
||||
int objectForId(const CompiledObject &component, int id) const;
|
||||
|
||||
QQmlPropertyCacheVector *propertyCaches;
|
||||
|
@ -772,107 +748,11 @@ private:
|
|||
};
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
|
||||
inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(
|
||||
QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
|
||||
: propertyCaches(propertyCaches)
|
||||
, objectContainer(objectContainer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
|
||||
{
|
||||
// skip the root object (index 0) as that one does not have a first object index originating
|
||||
// from a binding.
|
||||
for (int i = 1; i < objectContainer->objectCount(); ++i) {
|
||||
const CompiledObject &component = *objectContainer->objectAt(i);
|
||||
if (!component.hasFlag(QV4::CompiledData::Object::IsComponent))
|
||||
continue;
|
||||
|
||||
const auto rootBinding = component.bindingsBegin();
|
||||
appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv);
|
||||
}
|
||||
|
||||
const int rootObjectIndex = 0;
|
||||
appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv);
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
|
||||
{
|
||||
QVector<int> objectsWithAliases;
|
||||
collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
|
||||
if (objectsWithAliases.isEmpty())
|
||||
return;
|
||||
|
||||
const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
|
||||
auto alias = object.aliasesBegin();
|
||||
auto end = object.aliasesEnd();
|
||||
for ( ; alias != end; ++alias) {
|
||||
Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
|
||||
|
||||
const int targetObjectIndex = objectForId(component, alias->targetObjectId());
|
||||
Q_ASSERT(targetObjectIndex >= 0);
|
||||
|
||||
if (alias->isAliasToLocalAlias())
|
||||
continue;
|
||||
|
||||
if (alias->encodedMetaPropertyIndex == -1)
|
||||
continue;
|
||||
|
||||
const QQmlPropertyCache::ConstPtr targetCache
|
||||
= propertyCaches->at(targetObjectIndex);
|
||||
Q_ASSERT(targetCache);
|
||||
|
||||
int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
|
||||
const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
|
||||
if (!targetProperty)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
do {
|
||||
QVector<int> pendingObjects;
|
||||
|
||||
for (int objectIndex: std::as_const(objectsWithAliases)) {
|
||||
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
|
||||
|
||||
if (allAliasTargetsExist(object)) {
|
||||
appendAliasesToPropertyCache(component, objectIndex, enginePriv);
|
||||
} else {
|
||||
pendingObjects.append(objectIndex);
|
||||
}
|
||||
|
||||
}
|
||||
objectsWithAliases = std::move(pendingObjects);
|
||||
} while (!objectsWithAliases.isEmpty());
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
|
||||
{
|
||||
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
|
||||
if (object.aliasCount() > 0)
|
||||
objectsWithAliases->append(objectIndex);
|
||||
|
||||
// Stop at Component boundary
|
||||
if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
|
||||
return;
|
||||
|
||||
auto binding = object.bindingsBegin();
|
||||
auto end = object.bindingsEnd();
|
||||
for (; binding != end; ++binding) {
|
||||
switch (binding->type()) {
|
||||
case QV4::CompiledData::Binding::Type_Object:
|
||||
case QV4::CompiledData::Binding::Type_AttachedProperty:
|
||||
case QV4::CompiledData::Binding::Type_GroupProperty:
|
||||
collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ObjectContainer>
|
||||
|
@ -912,7 +792,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
|
|||
lastAlias = targetAlias;
|
||||
} while (lastAlias->isAliasToLocalAlias());
|
||||
|
||||
return propertyDataForAlias(component, *lastAlias, type, version, propertyFlags, enginePriv);
|
||||
return propertyDataForAlias(
|
||||
component, *lastAlias, type, version, propertyFlags, enginePriv);
|
||||
}
|
||||
|
||||
const int targetObjectIndex = objectForId(component, alias.targetObjectId());
|
||||
|
@ -947,7 +828,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
|
|||
propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
|
||||
} else {
|
||||
int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
|
||||
int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
|
||||
int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
|
||||
alias.encodedMetaPropertyIndex).valueTypeIndex();
|
||||
|
||||
QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
|
||||
Q_ASSERT(targetCache);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <private/qqmlvmemetaobject_p.h>
|
||||
#include <private/qqmlcomponent_p.h>
|
||||
#include <private/qqmlpropertyresolver_p.h>
|
||||
#include <private/qqmlcomponentandaliasresolver_p.h>
|
||||
|
||||
#define COMPILE_EXCEPTION(token, desc) \
|
||||
{ \
|
||||
|
@ -63,9 +64,11 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
|
|||
} else {
|
||||
// Resolve component boundaries and aliases
|
||||
|
||||
QQmlComponentAndAliasResolver resolver(this);
|
||||
if (!resolver.resolve(result.processedRoot))
|
||||
QQmlComponentAndAliasResolver resolver(this, enginePrivate(), &m_propertyCaches);
|
||||
if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) {
|
||||
recordError(error);
|
||||
return nullptr;
|
||||
}
|
||||
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches);
|
||||
pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
|
||||
}
|
||||
|
@ -202,10 +205,9 @@ QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
|
|||
return &document->objects;
|
||||
}
|
||||
|
||||
void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
|
||||
QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches()
|
||||
{
|
||||
m_propertyCaches = std::move(caches);
|
||||
Q_ASSERT(m_propertyCaches.count() > 0);
|
||||
return &m_propertyCaches;
|
||||
}
|
||||
|
||||
const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
|
||||
|
@ -213,11 +215,6 @@ const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
|
|||
return &m_propertyCaches;
|
||||
}
|
||||
|
||||
QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
|
||||
{
|
||||
return std::move(m_propertyCaches);
|
||||
}
|
||||
|
||||
QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
|
||||
{
|
||||
return document->jsParserEngine.pool();
|
||||
|
@ -730,357 +727,86 @@ void QQmlScriptStringScanner::scan()
|
|||
}
|
||||
}
|
||||
|
||||
QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
|
||||
: QQmlCompilePass(typeCompiler)
|
||||
, enginePrivate(typeCompiler->enginePrivate())
|
||||
, pool(typeCompiler->memoryPool())
|
||||
, qmlObjects(typeCompiler->qmlObjects())
|
||||
, propertyCaches(std::move(typeCompiler->takePropertyCaches()))
|
||||
template<>
|
||||
void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects(
|
||||
QmlIR::Object *object) const
|
||||
{
|
||||
object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex);
|
||||
}
|
||||
|
||||
static bool isUsableComponent(const QMetaObject *metaObject)
|
||||
template<>
|
||||
bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const
|
||||
{
|
||||
// The metaObject is a component we're interested in if it either is a QQmlComponent itself
|
||||
// or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
|
||||
// qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
|
||||
|
||||
if (metaObject == &QQmlComponent::staticMetaObject)
|
||||
return true;
|
||||
|
||||
for (; metaObject; metaObject = metaObject->superClass()) {
|
||||
if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(
|
||||
const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
|
||||
{
|
||||
QQmlPropertyResolver propertyResolver(propertyCache);
|
||||
|
||||
const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
|
||||
|
||||
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
|
||||
if (binding->type() != QV4::CompiledData::Binding::Type_Object)
|
||||
continue;
|
||||
if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
|
||||
continue;
|
||||
|
||||
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
|
||||
auto *tr = resolvedType(targetObject->inheritedTypeNameIndex);
|
||||
Q_ASSERT(tr);
|
||||
|
||||
const QMetaObject *firstMetaObject = nullptr;
|
||||
const auto type = tr->type();
|
||||
if (type.isValid())
|
||||
firstMetaObject = type.metaObject();
|
||||
else if (const auto compilationUnit = tr->compilationUnit())
|
||||
firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
|
||||
if (isUsableComponent(firstMetaObject))
|
||||
continue;
|
||||
// if here, not a QQmlComponent, so needs wrapping
|
||||
|
||||
const QQmlPropertyData *pd = nullptr;
|
||||
if (binding->propertyNameIndex != quint32(0)) {
|
||||
bool notInRevision = false;
|
||||
pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision);
|
||||
} else {
|
||||
pd = defaultProperty;
|
||||
}
|
||||
if (!pd || !pd->isQObject())
|
||||
continue;
|
||||
|
||||
// If the version is given, use it and look up by QQmlType.
|
||||
// Otherwise, make sure we look up by metaobject.
|
||||
// TODO: Is this correct?
|
||||
QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
|
||||
? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
|
||||
: QQmlMetaType::rawPropertyCacheForType(pd->propType());
|
||||
const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
|
||||
while (mo) {
|
||||
if (mo == &QQmlComponent::staticMetaObject)
|
||||
break;
|
||||
mo = mo->superClass();
|
||||
}
|
||||
|
||||
if (!mo)
|
||||
continue;
|
||||
|
||||
// emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
|
||||
QQmlType componentType = QQmlMetaType::qmlType(
|
||||
&QQmlComponent::staticMetaObject, QStringLiteral("QML"),
|
||||
QTypeRevision::fromVersion(1, 0));
|
||||
Q_ASSERT(componentType.isValid());
|
||||
const QString qualifier = QStringLiteral("QML");
|
||||
|
||||
compiler->addImport(componentType.module(), qualifier, componentType.version());
|
||||
|
||||
QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
|
||||
syntheticComponent->init(
|
||||
pool,
|
||||
compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()),
|
||||
compiler->registerString(QString()), binding->valueLocation);
|
||||
syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
|
||||
|
||||
if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
|
||||
auto typeRef = new QV4::ResolvedTypeReference;
|
||||
typeRef->setType(componentType);
|
||||
typeRef->setVersion(componentType.version());
|
||||
insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef);
|
||||
}
|
||||
|
||||
qmlObjects->append(syntheticComponent);
|
||||
const int componentIndex = qmlObjects->size() - 1;
|
||||
// Keep property caches symmetric
|
||||
QQmlPropertyCache::ConstPtr componentCache
|
||||
= QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
|
||||
propertyCaches.append(componentCache);
|
||||
|
||||
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
|
||||
*syntheticBinding = *binding;
|
||||
|
||||
// The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
|
||||
syntheticBinding->propertyNameIndex = 0;
|
||||
|
||||
syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
|
||||
QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
|
||||
Q_ASSERT(error.isEmpty());
|
||||
Q_UNUSED(error);
|
||||
|
||||
binding->value.objectIndex = componentIndex;
|
||||
|
||||
componentRoots.append(componentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve ignores everything relating to inline components, except for implicit components.
|
||||
bool QQmlComponentAndAliasResolver::resolve(int root)
|
||||
{
|
||||
// Detect real Component {} objects as well as implicitly defined components, such as
|
||||
// someItemDelegate: Item {}
|
||||
// In the implicit case Item is surrounded by a synthetic Component {} because the property
|
||||
// on the left hand side is of QQmlComponent type.
|
||||
const int objCountWithoutSynthesizedComponents = qmlObjects->size();
|
||||
const int startObjectIndex = root == 0 ? root : root+1; // root+1, as ic root is handled at the end
|
||||
for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
|
||||
QmlIR::Object *obj = qmlObjects->at(i);
|
||||
const bool isInlineComponentRoot
|
||||
= obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
|
||||
const bool isPartOfInlineComponent
|
||||
= obj->flags & QV4::CompiledData::Object::IsPartOfInlineComponent;
|
||||
QQmlPropertyCache::ConstPtr cache = propertyCaches.at(i);
|
||||
|
||||
bool isExplicitComponent = false;
|
||||
if (obj->inheritedTypeNameIndex) {
|
||||
auto *tref = resolvedType(obj->inheritedTypeNameIndex);
|
||||
Q_ASSERT(tref);
|
||||
if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
|
||||
isExplicitComponent = true;
|
||||
}
|
||||
|
||||
if (isInlineComponentRoot && isExplicitComponent) {
|
||||
qCWarning(lcQmlTypeCompiler).nospace().noquote()
|
||||
<< compiler->url().toString() << ":" << obj->location.line() << ":"
|
||||
<< obj->location.column()
|
||||
<< ": Using a Component as the root of an inline component is deprecated: "
|
||||
"inline components are "
|
||||
"automatically wrapped into Components when needed.";
|
||||
}
|
||||
|
||||
if (root == 0) {
|
||||
// normal component root, skip over anything inline component related
|
||||
if (isInlineComponentRoot || isPartOfInlineComponent)
|
||||
continue;
|
||||
} else if (!isPartOfInlineComponent || isInlineComponentRoot) {
|
||||
// We've left the current inline component (potentially entered a new one),
|
||||
// but we still need to resolve implicit components which are part of inline components.
|
||||
if (cache && !isExplicitComponent)
|
||||
findAndRegisterImplicitComponents(obj, cache);
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj->inheritedTypeNameIndex == 0 && !cache)
|
||||
continue;
|
||||
|
||||
if (!isExplicitComponent) {
|
||||
if (cache)
|
||||
findAndRegisterImplicitComponents(obj, cache);
|
||||
continue;
|
||||
}
|
||||
|
||||
obj->flags |= QV4::CompiledData::Object::IsComponent;
|
||||
|
||||
// check if this object is the root
|
||||
if (i == 0) {
|
||||
if (isExplicitComponent)
|
||||
qCWarning(lcQmlTypeCompiler).nospace().noquote()
|
||||
<< compiler->url().toString() << ":" << obj->location.line() << ":"
|
||||
<< obj->location.column()
|
||||
<< ": Using a Component as the root of a qmldocument is deprecated: types "
|
||||
"defined in qml documents are "
|
||||
"automatically wrapped into Components when needed.";
|
||||
}
|
||||
|
||||
if (obj->functionCount() > 0)
|
||||
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
|
||||
if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
|
||||
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
|
||||
if (obj->signalCount() > 0)
|
||||
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
|
||||
|
||||
if (obj->bindingCount() == 0)
|
||||
COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
|
||||
|
||||
const QmlIR::Binding *rootBinding = obj->firstBinding();
|
||||
|
||||
for (const QmlIR::Binding *b = rootBinding; b; b = b->next) {
|
||||
if (b->propertyNameIndex != 0)
|
||||
COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
|
||||
}
|
||||
|
||||
if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object)
|
||||
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
|
||||
|
||||
// For the root object, we are going to collect ids/aliases and resolve them for as a separate
|
||||
// last pass.
|
||||
if (i != 0)
|
||||
componentRoots.append(i);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < componentRoots.size(); ++i) {
|
||||
QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
|
||||
const QmlIR::Binding *rootBinding = component->firstBinding();
|
||||
|
||||
_idToObjectIndex.clear();
|
||||
|
||||
_objectsWithAliases.clear();
|
||||
|
||||
if (!collectIdsAndAliases(rootBinding->value.objectIndex))
|
||||
return false;
|
||||
|
||||
component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
|
||||
|
||||
if (!resolveAliases(componentRoots.at(i)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Collect ids and aliases for root
|
||||
_idToObjectIndex.clear();
|
||||
_objectsWithAliases.clear();
|
||||
|
||||
collectIdsAndAliases(root);
|
||||
|
||||
QmlIR::Object *rootComponent = qmlObjects->at(root);
|
||||
rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
|
||||
|
||||
if (!resolveAliases(root))
|
||||
return false;
|
||||
|
||||
// Implicit component insertion may have added objects and thus we also need
|
||||
// to extend the symmetric propertyCaches.
|
||||
compiler->setPropertyCaches(std::move(propertyCaches));
|
||||
compiler->setComponentRoots(componentRoots);
|
||||
|
||||
m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
|
||||
template<>
|
||||
void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const
|
||||
{
|
||||
QmlIR::Object *obj = qmlObjects->at(objectIndex);
|
||||
m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size();
|
||||
}
|
||||
|
||||
if (obj->idNameIndex != 0) {
|
||||
if (_idToObjectIndex.contains(obj->idNameIndex)) {
|
||||
recordError(obj->locationOfIdProperty, tr("id is not unique"));
|
||||
return false;
|
||||
}
|
||||
obj->id = _idToObjectIndex.size();
|
||||
_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
|
||||
template<>
|
||||
bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlIR::Binding *binding)
|
||||
{
|
||||
QQmlJS::MemoryPool *pool = m_compiler->memoryPool();
|
||||
QVector<QmlIR::Object *> *qmlObjects = m_compiler->qmlObjects();
|
||||
|
||||
// emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
|
||||
QQmlType componentType = QQmlMetaType::qmlType(
|
||||
&QQmlComponent::staticMetaObject, QStringLiteral("QML"),
|
||||
QTypeRevision::fromVersion(1, 0));
|
||||
Q_ASSERT(componentType.isValid());
|
||||
const QString qualifier = QStringLiteral("QML");
|
||||
|
||||
m_compiler->addImport(componentType.module(), qualifier, componentType.version());
|
||||
|
||||
QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
|
||||
syntheticComponent->init(
|
||||
pool,
|
||||
m_compiler->registerString(
|
||||
qualifier + QLatin1Char('.') + componentType.elementName()),
|
||||
m_compiler->registerString(QString()), binding->valueLocation);
|
||||
syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
|
||||
|
||||
if (!m_compiler->resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
|
||||
auto typeRef = new QV4::ResolvedTypeReference;
|
||||
typeRef->setType(componentType);
|
||||
typeRef->setVersion(componentType.version());
|
||||
m_compiler->resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
|
||||
}
|
||||
|
||||
if (obj->aliasCount() > 0)
|
||||
_objectsWithAliases.append(objectIndex);
|
||||
qmlObjects->append(syntheticComponent);
|
||||
const int componentIndex = qmlObjects->size() - 1;
|
||||
// Keep property caches symmetric
|
||||
QQmlPropertyCache::ConstPtr componentCache
|
||||
= QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
|
||||
m_propertyCaches->append(componentCache);
|
||||
|
||||
// Stop at Component boundary
|
||||
if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
|
||||
return true;
|
||||
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
|
||||
*syntheticBinding = *binding;
|
||||
|
||||
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
|
||||
switch (binding->type()) {
|
||||
case QV4::CompiledData::Binding::Type_Object:
|
||||
case QV4::CompiledData::Binding::Type_AttachedProperty:
|
||||
case QV4::CompiledData::Binding::Type_GroupProperty:
|
||||
if (!collectIdsAndAliases(binding->value.objectIndex))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
|
||||
syntheticBinding->propertyNameIndex = 0;
|
||||
|
||||
syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
|
||||
QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
|
||||
Q_ASSERT(error.isEmpty());
|
||||
Q_UNUSED(error);
|
||||
|
||||
binding->value.objectIndex = componentIndex;
|
||||
|
||||
m_componentRoots.append(componentIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
|
||||
template<>
|
||||
typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
|
||||
QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
|
||||
int objectIndex, QQmlError *error)
|
||||
{
|
||||
if (_objectsWithAliases.isEmpty())
|
||||
return true;
|
||||
|
||||
QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
|
||||
|
||||
bool atLeastOneAliasResolved;
|
||||
do {
|
||||
atLeastOneAliasResolved = false;
|
||||
QVector<int> pendingObjects;
|
||||
|
||||
for (int objectIndex: std::as_const(_objectsWithAliases)) {
|
||||
|
||||
QQmlError error;
|
||||
const auto result = resolveAliasesInObject(objectIndex, &error);
|
||||
|
||||
if (error.isValid()) {
|
||||
recordError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result == AllAliasesResolved) {
|
||||
QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
|
||||
if (error.isValid()) {
|
||||
recordError(error);
|
||||
return false;
|
||||
}
|
||||
atLeastOneAliasResolved = true;
|
||||
} else if (result == SomeAliasesResolved) {
|
||||
atLeastOneAliasResolved = true;
|
||||
pendingObjects.append(objectIndex);
|
||||
} else {
|
||||
pendingObjects.append(objectIndex);
|
||||
}
|
||||
}
|
||||
qSwap(_objectsWithAliases, pendingObjects);
|
||||
} while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
|
||||
|
||||
if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
|
||||
const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
|
||||
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
|
||||
if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
|
||||
recordError(alias->location, tr("Circular alias reference detected"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QQmlComponentAndAliasResolver::AliasResolutionResult
|
||||
QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
|
||||
QQmlError *error)
|
||||
{
|
||||
const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
|
||||
const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex);
|
||||
if (!obj->aliasCount())
|
||||
return AllAliasesResolved;
|
||||
|
||||
|
@ -1094,7 +820,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
|
|||
seenUnresolvedAlias = true;
|
||||
|
||||
const int idIndex = alias->idIndex();
|
||||
const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
|
||||
const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
|
||||
if (targetObjectIndex == -1) {
|
||||
*error = qQmlCompileError(
|
||||
alias->referenceLocation,
|
||||
|
@ -1102,7 +828,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
|
|||
break;
|
||||
}
|
||||
|
||||
const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
|
||||
const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
|
||||
Q_ASSERT(targetObject->id >= 0);
|
||||
alias->setTargetObjectId(targetObject->id);
|
||||
alias->setIsAliasToLocalAlias(false);
|
||||
|
@ -1124,7 +850,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
|
|||
if (property.isEmpty()) {
|
||||
alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
|
||||
} else {
|
||||
QQmlPropertyCache::ConstPtr targetCache = propertyCaches.at(targetObjectIndex);
|
||||
QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
|
||||
if (!targetCache) {
|
||||
*error = qQmlCompileError(
|
||||
alias->referenceLocation,
|
||||
|
@ -1180,8 +906,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
|
|||
isDeepAlias = false;
|
||||
for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
|
||||
auto binding = *it;
|
||||
if (compiler->stringAt(binding.propertyNameIndex) == property) {
|
||||
resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex));
|
||||
if (m_compiler->stringAt(binding.propertyNameIndex) == property) {
|
||||
resolver = QQmlPropertyResolver(m_propertyCaches->at(binding.value.objectIndex));
|
||||
const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
|
||||
if (actualProperty) {
|
||||
propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
|
||||
|
|
|
@ -52,9 +52,14 @@ public:
|
|||
|
||||
// --- interface used by QQmlPropertyCacheCreator
|
||||
typedef QmlIR::Object CompiledObject;
|
||||
typedef QmlIR::Binding CompiledBinding;
|
||||
using ListPropertyAssignBehavior = QmlIR::Pragma::ListPropertyAssignBehaviorValue;
|
||||
|
||||
// Deliberate choice of map over hash here to ensure stable generated output.
|
||||
using IdToObjectMap = QMap<int, int>;
|
||||
|
||||
const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
|
||||
QmlIR::Object *objectAt(int index) { return document->objects.at(index); }
|
||||
int objectCount() const { return document->objects.size(); }
|
||||
QString stringAt(int idx) const;
|
||||
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
|
||||
|
@ -86,11 +91,8 @@ public:
|
|||
QQmlEnginePrivate *enginePrivate() const { return engine; }
|
||||
const QQmlImports *imports() const;
|
||||
QVector<QmlIR::Object *> *qmlObjects() const;
|
||||
void setPropertyCaches(QQmlPropertyCacheVector &&caches);
|
||||
QQmlPropertyCacheVector *propertyCaches();
|
||||
const QQmlPropertyCacheVector *propertyCaches() const;
|
||||
QQmlPropertyCacheVector &&takePropertyCaches();
|
||||
void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
|
||||
const QVector<quint32> &componentRoots() const { return m_componentRoots; }
|
||||
QQmlJS::MemoryPool *memoryPool();
|
||||
QStringView newStringRef(const QString &string);
|
||||
const QV4::Compiler::StringTableGenerator *stringPool() const;
|
||||
|
@ -117,7 +119,6 @@ private:
|
|||
QHash<int, QQmlCustomParser*> customParsers;
|
||||
|
||||
// index in first hash is component index, vector inside contains object indices of objects with id property
|
||||
QVector<quint32> m_componentRoots;
|
||||
QQmlPropertyCacheVector m_propertyCaches;
|
||||
|
||||
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
|
||||
|
@ -132,16 +133,9 @@ struct QQmlCompilePass
|
|||
protected:
|
||||
void recordError(const QV4::CompiledData::Location &location, const QString &description) const
|
||||
{ compiler->recordError(location, description); }
|
||||
void recordError(const QQmlError &error)
|
||||
{ compiler->recordError(error); }
|
||||
|
||||
QV4::ResolvedTypeReference *resolvedType(int id) const
|
||||
{ return compiler->resolvedType(id); }
|
||||
bool containsResolvedType(int id) const
|
||||
{ return compiler->resolvedTypes->contains(id); }
|
||||
QV4::ResolvedTypeReferenceMap::iterator insertResolvedType(
|
||||
int id, QV4::ResolvedTypeReference *value)
|
||||
{ return compiler->resolvedTypes->insert(id, value); }
|
||||
|
||||
QQmlTypeCompiler *compiler;
|
||||
};
|
||||
|
@ -235,44 +229,6 @@ private:
|
|||
const QQmlPropertyCacheVector * const propertyCaches;
|
||||
};
|
||||
|
||||
class QQmlComponentAndAliasResolver : public QQmlCompilePass
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver)
|
||||
public:
|
||||
QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler);
|
||||
|
||||
bool resolve(int root = 0);
|
||||
|
||||
protected:
|
||||
void findAndRegisterImplicitComponents(
|
||||
const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
|
||||
bool collectIdsAndAliases(int objectIndex);
|
||||
bool resolveAliases(int componentIndex);
|
||||
void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
|
||||
|
||||
enum AliasResolutionResult {
|
||||
NoAliasResolved,
|
||||
SomeAliasesResolved,
|
||||
AllAliasesResolved
|
||||
};
|
||||
|
||||
AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
|
||||
|
||||
QQmlEnginePrivate *enginePrivate;
|
||||
QQmlJS::MemoryPool *pool;
|
||||
|
||||
QVector<QmlIR::Object*> *qmlObjects;
|
||||
|
||||
// indices of the objects that are actually Component {}
|
||||
QVector<quint32> componentRoots;
|
||||
|
||||
// Deliberate choice of map over hash here to ensure stable generated output.
|
||||
QMap<int, int> _idToObjectIndex;
|
||||
QVector<int> _objectsWithAliases;
|
||||
|
||||
QQmlPropertyCacheVector propertyCaches;
|
||||
};
|
||||
|
||||
class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QQmlDeferredAndCustomParserBindingScanner)
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <private/qqmltypedata_p.h>
|
||||
#include <private/qqmlcomponentandaliasresolver_p.h>
|
||||
#include <private/qqmlengine_p.h>
|
||||
#include <private/qqmlpropertycachecreator_p.h>
|
||||
#include <private/qqmlpropertyvalidator_p.h>
|
||||
#include <private/qqmlirbuilder_p.h>
|
||||
#include <private/qqmlirloader_p.h>
|
||||
#include <private/qqmlpropertycachecreator_p.h>
|
||||
#include <private/qqmlpropertyvalidator_p.h>
|
||||
#include <private/qqmlscriptblob_p.h>
|
||||
#include <private/qqmlscriptdata_p.h>
|
||||
#include <private/qqmltypecompiler_p.h>
|
||||
#include <private/qqmltypedata_p.h>
|
||||
#include <private/qqmltypeloaderqmldircontent_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
|
@ -173,7 +174,53 @@ bool QQmlTypeData::tryLoadFromDiskCache()
|
|||
return true;
|
||||
}
|
||||
|
||||
void QQmlTypeData::createTypeAndPropertyCaches(
|
||||
template<>
|
||||
void QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::allocateNamedObjects(
|
||||
const QV4::CompiledData::Object *object) const
|
||||
{
|
||||
Q_UNUSED(object);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::markAsComponent(int index) const
|
||||
{
|
||||
return m_compiler->objectAt(index)->hasFlag(QV4::CompiledData::Object::IsComponent);
|
||||
}
|
||||
|
||||
template<>
|
||||
void QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::setObjectId(int index) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
// we cannot sanity-check the index here because bindings are sorted in a different order
|
||||
// in the CU vs the IR.
|
||||
}
|
||||
|
||||
template<>
|
||||
typename QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::AliasResolutionResult
|
||||
QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::resolveAliasesInObject(
|
||||
int objectIndex, QQmlError *error)
|
||||
{
|
||||
const CompiledObject *obj = m_compiler->objectAt(objectIndex);
|
||||
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
|
||||
if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
|
||||
*error = qQmlCompileError( alias->referenceLocation, tr("Unresolved alias found"));
|
||||
return NoAliasResolved;
|
||||
}
|
||||
}
|
||||
|
||||
return AllAliasesResolved;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::wrapImplicitComponent(
|
||||
const QV4::CompiledData::Binding *binding)
|
||||
{
|
||||
// This should have been done when creating the CU.
|
||||
Q_UNUSED(binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
QQmlError QQmlTypeData::createTypeAndPropertyCaches(
|
||||
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
|
||||
const QV4::ResolvedTypeReferenceMap &resolvedTypeCache)
|
||||
{
|
||||
|
@ -190,18 +237,32 @@ void QQmlTypeData::createTypeAndPropertyCaches(
|
|||
QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
|
||||
&m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
|
||||
m_compiledData.data(), m_importCache.data(), typeClassName());
|
||||
QQmlError error = propertyCacheCreator.buildMetaObjects();
|
||||
if (error.isValid()) {
|
||||
setError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
|
||||
&m_compiledData->propertyCaches, m_compiledData.data());
|
||||
aliasCreator.appendAliasPropertiesToMetaObjects(engine);
|
||||
QQmlError error = propertyCacheCreator.verifyNoICCycle();
|
||||
if (error.isValid())
|
||||
return error;
|
||||
|
||||
QQmlPropertyCacheCreatorBase::IncrementalResult result;
|
||||
do {
|
||||
result = propertyCacheCreator.buildMetaObjectsIncrementally();
|
||||
if (result.error.isValid()) {
|
||||
return result.error;
|
||||
} else {
|
||||
QQmlComponentAndAliasResolver resolver(
|
||||
m_compiledData.data(), engine, &m_compiledData->propertyCaches);
|
||||
if (const QQmlError error = resolver.resolve(result.processedRoot);
|
||||
error.isValid()) {
|
||||
return error;
|
||||
}
|
||||
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
|
||||
pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
|
||||
}
|
||||
|
||||
} while (result.canResume);
|
||||
}
|
||||
|
||||
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
|
||||
return QQmlError();
|
||||
}
|
||||
|
||||
static bool addTypeReferenceChecksumsToHash(
|
||||
|
@ -370,23 +431,34 @@ void QQmlTypeData::done()
|
|||
|
||||
// verify if any dependencies changed if we're using a cache
|
||||
if (m_document.isNull()) {
|
||||
createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
|
||||
if (isError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_compiledData->verifyChecksum(dependencyHasher)) {
|
||||
const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
|
||||
if (!error.isValid() && m_compiledData->verifyChecksum(dependencyHasher)) {
|
||||
setCompileUnit(m_compiledData);
|
||||
} else {
|
||||
qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if (!loadFromSource())
|
||||
return;
|
||||
|
||||
// We want to keep our resolve types ...
|
||||
m_compiledData->resolvedTypes.clear();
|
||||
// ... but we don't want their property caches.
|
||||
for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache))
|
||||
// ... but we don't want the property caches we've created for the broken CU.
|
||||
for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache)) {
|
||||
if (ref->compilationUnit() != m_compiledData)
|
||||
continue;
|
||||
ref->setTypePropertyCache(QQmlPropertyCache::ConstPtr());
|
||||
ref->setCompilationUnit(QQmlRefPointer<QV4::ExecutableCompilationUnit>());
|
||||
}
|
||||
|
||||
m_compiledData.reset();
|
||||
}
|
||||
|
|
|
@ -95,8 +95,8 @@ private:
|
|||
void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
|
||||
QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
|
||||
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
|
||||
void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
|
||||
const QV4::ResolvedTypeReferenceMap &resolvedTypeCache);
|
||||
QQmlError createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
|
||||
const QV4::ResolvedTypeReferenceMap &resolvedTypeCache);
|
||||
bool resolveType(const QString &typeName, QTypeRevision &version,
|
||||
TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
|
||||
bool reportErrors = true,
|
||||
|
|
|
@ -41,6 +41,8 @@ private slots:
|
|||
void cacheModuleScripts();
|
||||
void reuseStaticMappings();
|
||||
void invalidateSaveLoadCache();
|
||||
|
||||
void inlineComponentDoesNotCauseConstantInvalidation_data();
|
||||
void inlineComponentDoesNotCauseConstantInvalidation();
|
||||
|
||||
private:
|
||||
|
@ -1118,29 +1120,123 @@ void tst_qmldiskcache::invalidateSaveLoadCache()
|
|||
QVERIFY(unit->unitData() != oldUnit->unitData());
|
||||
}
|
||||
|
||||
void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("code");
|
||||
|
||||
QTest::addRow("simple") << QByteArray(R"(
|
||||
import QtQml
|
||||
QtObject {
|
||||
component Test: QtObject {
|
||||
property int i: 28
|
||||
}
|
||||
property Test test: Test {
|
||||
objectName: "foobar"
|
||||
}
|
||||
property int k: test.i
|
||||
}
|
||||
)");
|
||||
|
||||
QTest::addRow("with function") << QByteArray(R"(
|
||||
import QtQml
|
||||
QtObject {
|
||||
component Test : QtObject {
|
||||
id: self
|
||||
property int i: 2
|
||||
property alias j: self.i
|
||||
}
|
||||
property Test test: Test {
|
||||
function updateValue() {}
|
||||
objectName: 'foobar'
|
||||
j: 28
|
||||
}
|
||||
property int k: test.j
|
||||
}
|
||||
)");
|
||||
|
||||
QTest::addRow("in nested") << QByteArray(R"(
|
||||
import QtQuick
|
||||
Item {
|
||||
Item {
|
||||
component Line: Item {
|
||||
property alias endY: pathLine.y
|
||||
Item {
|
||||
Item {
|
||||
id: pathLine
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Line {
|
||||
id: primaryLine
|
||||
endY: 28
|
||||
}
|
||||
property int k: primaryLine.endY
|
||||
}
|
||||
)");
|
||||
|
||||
QTest::addRow("with revision") << QByteArray(R"(
|
||||
import QtQuick
|
||||
ListView {
|
||||
Item {
|
||||
id: scrollBar
|
||||
}
|
||||
delegate: Image {
|
||||
mipmap: true
|
||||
}
|
||||
Item {
|
||||
id: refreshNodesIndicator
|
||||
}
|
||||
property int k: delegate.createObject().mipmap ? 28 : 4
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation()
|
||||
{
|
||||
QFETCH(QByteArray, code);
|
||||
|
||||
QQmlEngine engine;
|
||||
|
||||
TestCompiler testCompiler(&engine);
|
||||
QVERIFY(testCompiler.tempDir.isValid());
|
||||
|
||||
auto check = [&](){
|
||||
QQmlComponent c(&engine, QUrl::fromLocalFile(testCompiler.testFilePath));
|
||||
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
|
||||
QScopedPointer<QObject> o(c.create());
|
||||
QVERIFY(!o.isNull());
|
||||
QCOMPARE(o->property("k"), QVariant::fromValue<int>(28));
|
||||
};
|
||||
|
||||
testCompiler.reset();
|
||||
QVERIFY(testCompiler.writeTestFile("import QtQml\nQtObject { component Test : QtObject { property int i: 2 }\n property Test test: Test { objectName: 'foobar' } }\n"));
|
||||
QVERIFY(testCompiler.writeTestFile(code));
|
||||
|
||||
QVERIFY(testCompiler.loadTestFile());
|
||||
|
||||
const quintptr data1 = testCompiler.unitData();
|
||||
QVERIFY(data1 != 0);
|
||||
QCOMPARE(testCompiler.unitData(), data1);
|
||||
check();
|
||||
|
||||
engine.clearComponentCache();
|
||||
|
||||
// inline component does not invalidate cache
|
||||
QVERIFY(testCompiler.loadTestFile());
|
||||
QCOMPARE(testCompiler.unitData(), data1);
|
||||
check();
|
||||
|
||||
testCompiler.reset();
|
||||
QVERIFY(testCompiler.writeTestFile("import QtQml\nQtObject { component Test : QtObject { property double d: 2 }\n property Test test: Test { objectName: 'foobar' } }\n"));
|
||||
QVERIFY(testCompiler.writeTestFile(R"(
|
||||
import QtQml
|
||||
QtObject {
|
||||
component Test : QtObject {
|
||||
property double d: 2
|
||||
}
|
||||
property Test test: Test {
|
||||
objectName: 'foobar'
|
||||
}
|
||||
})"));
|
||||
QVERIFY(testCompiler.loadTestFile());
|
||||
const quintptr data2 = testCompiler.unitData();
|
||||
QVERIFY(data2);
|
||||
|
|
Loading…
Reference in New Issue