821 lines
35 KiB
C++
821 lines
35 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
#ifndef QQMLPROPERTYCACHECREATOR_P_H
|
|
#define QQMLPROPERTYCACHECREATOR_P_H
|
|
|
|
//
|
|
// W A R N I N G
|
|
// -------------
|
|
//
|
|
// This file is not part of the Qt API. It exists purely as an
|
|
// implementation detail. This header file may change from version to
|
|
// version without notice, or even be removed.
|
|
//
|
|
// We mean it.
|
|
//
|
|
|
|
#include <private/qqmlvaluetype_p.h>
|
|
#include <private/qqmlengine_p.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
struct QQmlBindingInstantiationContext {
|
|
QQmlBindingInstantiationContext() {}
|
|
QQmlBindingInstantiationContext(int referencingObjectIndex,
|
|
const QV4::CompiledData::Binding *instantiatingBinding,
|
|
const QString &instantiatingPropertyName,
|
|
QQmlPropertyCache *referencingObjectPropertyCache);
|
|
|
|
bool resolveInstantiatingProperty();
|
|
QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const;
|
|
|
|
int referencingObjectIndex = -1;
|
|
const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
|
|
QString instantiatingPropertyName;
|
|
QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache;
|
|
QQmlPropertyData *instantiatingProperty = nullptr;
|
|
};
|
|
|
|
struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
|
|
{
|
|
void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const;
|
|
};
|
|
|
|
struct QQmlPropertyCacheCreatorBase
|
|
{
|
|
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
|
|
public:
|
|
static QAtomicInt classIndexCounter;
|
|
};
|
|
|
|
template <typename ObjectContainer>
|
|
class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
|
|
{
|
|
public:
|
|
typedef typename ObjectContainer::CompiledObject CompiledObject;
|
|
|
|
QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
|
|
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
|
|
QQmlEnginePrivate *enginePrivate,
|
|
const ObjectContainer *objectContainer, const QQmlImports *imports);
|
|
|
|
QQmlCompileError buildMetaObjects();
|
|
|
|
protected:
|
|
QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
|
|
QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
|
|
QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
|
|
|
|
QString stringAt(int index) const { return objectContainer->stringAt(index); }
|
|
|
|
QQmlEnginePrivate * const enginePrivate;
|
|
const ObjectContainer * const objectContainer;
|
|
const QQmlImports * const imports;
|
|
QQmlPropertyCacheVector *propertyCaches;
|
|
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
|
|
};
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
|
|
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
|
|
QQmlEnginePrivate *enginePrivate,
|
|
const ObjectContainer *objectContainer, const QQmlImports *imports)
|
|
: enginePrivate(enginePrivate)
|
|
, objectContainer(objectContainer)
|
|
, imports(imports)
|
|
, propertyCaches(propertyCaches)
|
|
, pendingGroupPropertyBindings(pendingGroupPropertyBindings)
|
|
{
|
|
propertyCaches->resize(objectContainer->objectCount());
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
|
|
{
|
|
QQmlBindingInstantiationContext context;
|
|
return buildMetaObjectRecursively(/*root object*/0, context);
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
|
|
{
|
|
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
|
|
|
|
bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0;
|
|
if (!needVMEMetaObject) {
|
|
auto binding = obj->bindingsBegin();
|
|
auto end = obj->bindingsEnd();
|
|
for ( ; binding != end; ++binding) {
|
|
if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
|
|
// If the on assignment is inside a group property, we need to distinguish between QObject based
|
|
// group properties and value type group properties. For the former the base type is derived from
|
|
// the property that references us, for the latter we only need a meta-object on the referencing object
|
|
// because interceptors can't go to the shared value type instances.
|
|
if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) {
|
|
if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
|
|
const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
|
|
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
|
|
Q_ASSERT(typeRef);
|
|
QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
|
|
QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
|
|
if (error.isSet())
|
|
return error;
|
|
}
|
|
} else {
|
|
// On assignments are implemented using value interceptors, which require a VME meta object.
|
|
needVMEMetaObject = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
|
|
{
|
|
QQmlCompileError error;
|
|
baseTypeCache = propertyCacheForObject(obj, context, &error);
|
|
if (error.isSet())
|
|
return error;
|
|
}
|
|
|
|
if (baseTypeCache) {
|
|
if (needVMEMetaObject) {
|
|
QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache);
|
|
if (error.isSet())
|
|
return error;
|
|
} else {
|
|
propertyCaches->set(objectIndex, baseTypeCache);
|
|
}
|
|
}
|
|
|
|
if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
|
|
auto binding = obj->bindingsBegin();
|
|
auto end = obj->bindingsEnd();
|
|
for ( ; binding != end; ++binding)
|
|
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
|
|
QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
|
|
|
|
// Binding to group property where we failed to look up the type of the
|
|
// property? Possibly a group property that is an alias that's not resolved yet.
|
|
// Let's attempt to resolve it after we're done with the aliases and fill in the
|
|
// propertyCaches entry then.
|
|
if (!context.resolveInstantiatingProperty())
|
|
pendingGroupPropertyBindings->append(context);
|
|
|
|
QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
|
|
if (error.isSet())
|
|
return error;
|
|
}
|
|
}
|
|
|
|
QQmlCompileError noError;
|
|
return noError;
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
|
|
{
|
|
if (context.instantiatingProperty) {
|
|
return context.instantiatingPropertyCache(enginePrivate);
|
|
} else if (obj->inheritedTypeNameIndex != 0) {
|
|
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
|
|
Q_ASSERT(typeRef);
|
|
|
|
if (typeRef->isFullyDynamicType) {
|
|
if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
|
|
*error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
|
|
return nullptr;
|
|
}
|
|
if (obj->signalCount() > 0) {
|
|
*error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
|
|
return nullptr;
|
|
}
|
|
if (obj->functionCount() > 0) {
|
|
*error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
|
|
} else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
|
|
auto *typeRef = objectContainer->resolvedType(
|
|
context.instantiatingBinding->propertyNameIndex);
|
|
Q_ASSERT(typeRef);
|
|
QQmlType qmltype = typeRef->type;
|
|
if (!qmltype.isValid()) {
|
|
QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
|
|
if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
|
|
if (qmltype.isComposite()) {
|
|
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
|
|
Q_ASSERT(tdata);
|
|
Q_ASSERT(tdata->isComplete());
|
|
|
|
auto compilationUnit = tdata->compilationUnit();
|
|
qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
|
|
if (!attachedMo) {
|
|
*error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
|
|
return nullptr;
|
|
}
|
|
return enginePrivate->cache(attachedMo);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
|
|
{
|
|
QQmlRefPointer<QQmlPropertyCache> cache;
|
|
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
|
|
obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
|
|
obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount()));
|
|
|
|
propertyCaches->set(objectIndex, cache);
|
|
propertyCaches->setNeedsVMEMetaObject(objectIndex);
|
|
|
|
struct TypeData {
|
|
QV4::CompiledData::Property::Type dtype;
|
|
int metaType;
|
|
} builtinTypes[] = {
|
|
{ QV4::CompiledData::Property::Var, QMetaType::QVariant },
|
|
{ QV4::CompiledData::Property::Variant, QMetaType::QVariant },
|
|
{ QV4::CompiledData::Property::Int, QMetaType::Int },
|
|
{ QV4::CompiledData::Property::Bool, QMetaType::Bool },
|
|
{ QV4::CompiledData::Property::Real, QMetaType::Double },
|
|
{ QV4::CompiledData::Property::String, QMetaType::QString },
|
|
{ QV4::CompiledData::Property::Url, QMetaType::QUrl },
|
|
{ QV4::CompiledData::Property::Color, QMetaType::QColor },
|
|
{ QV4::CompiledData::Property::Font, QMetaType::QFont },
|
|
{ QV4::CompiledData::Property::Time, QMetaType::QTime },
|
|
{ QV4::CompiledData::Property::Date, QMetaType::QDate },
|
|
{ QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
|
|
{ QV4::CompiledData::Property::Rect, QMetaType::QRectF },
|
|
{ QV4::CompiledData::Property::Point, QMetaType::QPointF },
|
|
{ QV4::CompiledData::Property::Size, QMetaType::QSizeF },
|
|
{ QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
|
|
{ QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
|
|
{ QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
|
|
{ QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
|
|
{ QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
|
|
};
|
|
static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
|
|
|
|
QByteArray newClassName;
|
|
|
|
if (objectIndex == /*root object*/0) {
|
|
const QString path = objectContainer->url().path();
|
|
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
|
|
if (lastSlash > -1) {
|
|
const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
|
|
if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
|
|
newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
|
|
QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
|
|
}
|
|
}
|
|
if (newClassName.isEmpty()) {
|
|
newClassName = QQmlMetaObject(baseTypeCache.data()).className();
|
|
newClassName.append("_QML_");
|
|
newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
|
|
}
|
|
|
|
cache->_dynamicClassName = newClassName;
|
|
|
|
int varPropCount = 0;
|
|
|
|
QmlIR::PropertyResolver resolver(baseTypeCache);
|
|
|
|
auto p = obj->propertiesBegin();
|
|
auto pend = obj->propertiesEnd();
|
|
for ( ; p != pend; ++p) {
|
|
if (p->type == QV4::CompiledData::Property::Var)
|
|
varPropCount++;
|
|
|
|
bool notInRevision = false;
|
|
QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision);
|
|
if (d && d->isFinal())
|
|
return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
|
|
}
|
|
|
|
auto a = obj->aliasesBegin();
|
|
auto aend = obj->aliasesEnd();
|
|
for ( ; a != aend; ++a) {
|
|
bool notInRevision = false;
|
|
QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision);
|
|
if (d && d->isFinal())
|
|
return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
|
|
}
|
|
|
|
int effectivePropertyIndex = cache->propertyIndexCacheStart;
|
|
int effectiveMethodIndex = cache->methodIndexCacheStart;
|
|
|
|
// For property change signal override detection.
|
|
// We prepopulate a set of signal names which already exist in the object,
|
|
// and throw an error if there is a signal/method defined as an override.
|
|
QSet<QString> seenSignals;
|
|
seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
|
|
QQmlPropertyCache *parentCache = cache.data();
|
|
while ((parentCache = parentCache->parent())) {
|
|
if (int pSigCount = parentCache->signalCount()) {
|
|
int pSigOffset = parentCache->signalOffset();
|
|
for (int i = pSigOffset; i < pSigCount; ++i) {
|
|
QQmlPropertyData *currPSig = parentCache->signal(i);
|
|
// XXX TODO: find a better way to get signal name from the property data :-/
|
|
for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
|
|
iter != parentCache->stringCache.end(); ++iter) {
|
|
if (currPSig == (*iter).second) {
|
|
seenSignals.insert(iter.key());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set up notify signals for properties - first normal, then alias
|
|
p = obj->propertiesBegin();
|
|
pend = obj->propertiesEnd();
|
|
for ( ; p != pend; ++p) {
|
|
auto flags = QQmlPropertyData::defaultSignalFlags();
|
|
|
|
QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
|
|
seenSignals.insert(changedSigName);
|
|
|
|
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
|
|
}
|
|
|
|
a = obj->aliasesBegin();
|
|
aend = obj->aliasesEnd();
|
|
for ( ; a != aend; ++a) {
|
|
auto flags = QQmlPropertyData::defaultSignalFlags();
|
|
|
|
QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
|
|
seenSignals.insert(changedSigName);
|
|
|
|
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
|
|
}
|
|
|
|
auto e = obj->enumsBegin();
|
|
auto eend = obj->enumsEnd();
|
|
for ( ; e != eend; ++e) {
|
|
const int enumValueCount = e->enumValueCount();
|
|
QVector<QQmlEnumValue> values;
|
|
values.reserve(enumValueCount);
|
|
|
|
auto enumValue = e->enumValuesBegin();
|
|
auto end = e->enumValuesEnd();
|
|
for ( ; enumValue != end; ++enumValue)
|
|
values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value));
|
|
|
|
cache->appendEnum(stringAt(e->nameIndex), values);
|
|
}
|
|
|
|
// Dynamic signals
|
|
auto s = obj->signalsBegin();
|
|
auto send = obj->signalsEnd();
|
|
for ( ; s != send; ++s) {
|
|
const int paramCount = s->parameterCount();
|
|
|
|
QList<QByteArray> names;
|
|
names.reserve(paramCount);
|
|
QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
|
|
|
|
if (paramCount) {
|
|
paramTypes[0] = paramCount;
|
|
|
|
int i = 0;
|
|
auto param = s->parametersBegin();
|
|
auto end = s->parametersEnd();
|
|
for ( ; param != end; ++param, ++i) {
|
|
names.append(stringAt(param->nameIndex).toUtf8());
|
|
if (param->type < builtinTypeCount) {
|
|
// built-in type
|
|
paramTypes[i + 1] = builtinTypes[param->type].metaType;
|
|
} else {
|
|
// lazily resolved type
|
|
Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
|
|
const QString customTypeName = stringAt(param->customTypeNameIndex);
|
|
QQmlType qmltype;
|
|
if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr))
|
|
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
|
|
|
|
if (qmltype.isComposite()) {
|
|
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
|
|
Q_ASSERT(tdata);
|
|
Q_ASSERT(tdata->isComplete());
|
|
|
|
auto compilationUnit = tdata->compilationUnit();
|
|
|
|
paramTypes[i + 1] = compilationUnit->metaTypeId;
|
|
} else {
|
|
paramTypes[i + 1] = qmltype.typeId();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto flags = QQmlPropertyData::defaultSignalFlags();
|
|
if (paramCount)
|
|
flags.hasArguments = true;
|
|
|
|
QString signalName = stringAt(s->nameIndex);
|
|
if (seenSignals.contains(signalName))
|
|
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
|
|
seenSignals.insert(signalName);
|
|
|
|
cache->appendSignal(signalName, flags, effectiveMethodIndex++,
|
|
paramCount?paramTypes.constData():nullptr, names);
|
|
}
|
|
|
|
|
|
// Dynamic slots
|
|
auto function = objectContainer->objectFunctionsBegin(obj);
|
|
auto fend = objectContainer->objectFunctionsEnd(obj);
|
|
for ( ; function != fend; ++function) {
|
|
auto flags = QQmlPropertyData::defaultSlotFlags();
|
|
|
|
const QString slotName = stringAt(function->nameIndex);
|
|
if (seenSignals.contains(slotName))
|
|
return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
|
|
// Note: we don't append slotName to the seenSignals list, since we don't
|
|
// protect against overriding change signals or methods with properties.
|
|
|
|
QList<QByteArray> parameterNames;
|
|
auto formal = function->formalsBegin();
|
|
auto end = function->formalsEnd();
|
|
for ( ; formal != end; ++formal) {
|
|
flags.hasArguments = true;
|
|
parameterNames << stringAt(*formal).toUtf8();
|
|
}
|
|
|
|
cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
|
|
}
|
|
|
|
|
|
// Dynamic properties
|
|
int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
|
|
int propertyIdx = 0;
|
|
p = obj->propertiesBegin();
|
|
pend = obj->propertiesEnd();
|
|
for ( ; p != pend; ++p, ++propertyIdx) {
|
|
int propertyType = 0;
|
|
int propertTypeMinorVersion = 0;
|
|
QQmlPropertyData::Flags propertyFlags;
|
|
|
|
if (p->type == QV4::CompiledData::Property::Var) {
|
|
propertyType = QMetaType::QVariant;
|
|
propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
|
|
} else if (p->type < builtinTypeCount) {
|
|
propertyType = builtinTypes[p->type].metaType;
|
|
|
|
if (p->type == QV4::CompiledData::Property::Variant)
|
|
propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
|
|
} else {
|
|
Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
|
|
p->type == QV4::CompiledData::Property::Custom);
|
|
|
|
QQmlType qmltype;
|
|
if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
|
|
return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
|
|
}
|
|
|
|
Q_ASSERT(qmltype.isValid());
|
|
if (qmltype.isComposite()) {
|
|
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
|
|
Q_ASSERT(tdata);
|
|
Q_ASSERT(tdata->isComplete());
|
|
|
|
auto compilationUnit = tdata->compilationUnit();
|
|
|
|
if (p->type == QV4::CompiledData::Property::Custom) {
|
|
propertyType = compilationUnit->metaTypeId;
|
|
} else {
|
|
propertyType = compilationUnit->listMetaTypeId;
|
|
}
|
|
} else {
|
|
if (p->type == QV4::CompiledData::Property::Custom) {
|
|
propertyType = qmltype.typeId();
|
|
propertTypeMinorVersion = qmltype.minorVersion();
|
|
} else {
|
|
propertyType = qmltype.qListTypeId();
|
|
}
|
|
}
|
|
|
|
if (p->type == QV4::CompiledData::Property::Custom)
|
|
propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
|
|
else
|
|
propertyFlags.type = QQmlPropertyData::Flags::QListType;
|
|
}
|
|
|
|
if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
|
|
propertyFlags.isWritable = true;
|
|
|
|
|
|
QString propertyName = stringAt(p->nameIndex);
|
|
if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
|
|
cache->_defaultPropertyName = propertyName;
|
|
cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
|
|
propertyType, propertTypeMinorVersion, effectiveSignalIndex);
|
|
|
|
effectiveSignalIndex++;
|
|
}
|
|
|
|
QQmlCompileError noError;
|
|
return noError;
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
class QQmlPropertyCacheAliasCreator
|
|
{
|
|
public:
|
|
typedef typename ObjectContainer::CompiledObject CompiledObject;
|
|
|
|
QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
|
|
|
|
void appendAliasPropertiesToMetaObjects();
|
|
|
|
QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
|
|
|
|
private:
|
|
void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
|
|
QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags);
|
|
|
|
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
|
|
|
|
int objectForId(const CompiledObject &component, int id) const;
|
|
|
|
QQmlPropertyCacheVector *propertyCaches;
|
|
const ObjectContainer *objectContainer;
|
|
};
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
|
|
: propertyCaches(propertyCaches)
|
|
, objectContainer(objectContainer)
|
|
{
|
|
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
|
|
{
|
|
// 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.flags & QV4::CompiledData::Object::IsComponent))
|
|
continue;
|
|
|
|
const auto rootBinding = component.bindingsBegin();
|
|
appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
|
|
}
|
|
|
|
const int rootObjectIndex = 0;
|
|
appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
|
|
{
|
|
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->flags & QV4::CompiledData::Alias::Resolved);
|
|
|
|
const int targetObjectIndex = objectForId(component, alias->targetObjectId);
|
|
Q_ASSERT(targetObjectIndex >= 0);
|
|
|
|
if (alias->aliasToLocalAlias)
|
|
continue;
|
|
|
|
if (alias->encodedMetaPropertyIndex == -1)
|
|
continue;
|
|
|
|
const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
|
|
Q_ASSERT(targetCache);
|
|
|
|
int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
|
|
QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
|
|
if (!targetProperty)
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
do {
|
|
QVector<int> pendingObjects;
|
|
|
|
for (int objectIndex: qAsConst(objectsWithAliases)) {
|
|
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
|
|
|
|
if (allAliasTargetsExist(object)) {
|
|
appendAliasesToPropertyCache(component, objectIndex);
|
|
} else {
|
|
pendingObjects.append(objectIndex);
|
|
}
|
|
|
|
}
|
|
qSwap(objectsWithAliases, 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.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
|
|
return;
|
|
|
|
auto binding = object.bindingsBegin();
|
|
auto end = object.bindingsEnd();
|
|
for (; binding != end; ++binding) {
|
|
if (binding->type != QV4::CompiledData::Binding::Type_Object
|
|
&& binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
|
|
&& binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
|
|
continue;
|
|
|
|
collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
|
|
}
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
|
|
const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
|
|
QQmlPropertyData::Flags *propertyFlags)
|
|
{
|
|
const int targetObjectIndex = objectForId(component, alias.targetObjectId);
|
|
Q_ASSERT(targetObjectIndex >= 0);
|
|
|
|
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
|
|
|
|
*type = 0;
|
|
bool writable = false;
|
|
bool resettable = false;
|
|
|
|
propertyFlags->isAlias = true;
|
|
|
|
if (alias.aliasToLocalAlias) {
|
|
auto targetAlias = targetObject.aliasesBegin();
|
|
for (uint i = 0; i < alias.localAliasIndex; ++i)
|
|
++targetAlias;
|
|
return propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags);
|
|
} else if (alias.encodedMetaPropertyIndex == -1) {
|
|
Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
|
|
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
|
|
if (!typeRef) {
|
|
// Can be caused by the alias target not being a valid id or property. E.g.:
|
|
// property alias dataValue: dataVal
|
|
// invalidAliasComponent { id: dataVal }
|
|
return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
|
|
}
|
|
|
|
if (typeRef->type.isValid())
|
|
*type = typeRef->type.typeId();
|
|
else
|
|
*type = typeRef->compilationUnit->metaTypeId;
|
|
|
|
*minorVersion = typeRef->minorVersion;
|
|
|
|
propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
|
|
} else {
|
|
int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
|
|
int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
|
|
|
|
QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
|
|
Q_ASSERT(targetCache);
|
|
QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
|
|
Q_ASSERT(targetProperty);
|
|
|
|
*type = targetProperty->propType();
|
|
|
|
writable = targetProperty->isWritable();
|
|
resettable = targetProperty->isResettable();
|
|
|
|
if (valueTypeIndex != -1) {
|
|
const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
|
|
if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
|
|
*type = QVariant::Int;
|
|
else
|
|
*type = valueTypeMetaObject->property(valueTypeIndex).userType();
|
|
} else {
|
|
if (targetProperty->isEnum()) {
|
|
*type = QVariant::Int;
|
|
} else {
|
|
// Copy type flags
|
|
propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
|
|
|
|
if (targetProperty->isVarProperty())
|
|
propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
|
|
}
|
|
}
|
|
}
|
|
|
|
propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
|
|
propertyFlags->isResettable = resettable;
|
|
return QQmlCompileError();
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
|
|
const CompiledObject &component, int objectIndex)
|
|
{
|
|
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
|
|
if (!object.aliasCount())
|
|
return QQmlCompileError();
|
|
|
|
QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
|
|
Q_ASSERT(propertyCache);
|
|
|
|
int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
|
|
int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
|
|
|
|
int aliasIndex = 0;
|
|
auto alias = object.aliasesBegin();
|
|
auto end = object.aliasesEnd();
|
|
for ( ; alias != end; ++alias, ++aliasIndex) {
|
|
Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
|
|
|
|
int type = 0;
|
|
int minorVersion = 0;
|
|
QQmlPropertyData::Flags propertyFlags;
|
|
QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags);
|
|
if (error.isSet())
|
|
return error;
|
|
|
|
const QString propertyName = objectContainer->stringAt(alias->nameIndex);
|
|
|
|
if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
|
|
propertyCache->_defaultPropertyName = propertyName;
|
|
|
|
propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
|
|
type, minorVersion, effectiveSignalIndex++);
|
|
}
|
|
|
|
return QQmlCompileError();
|
|
}
|
|
|
|
template <typename ObjectContainer>
|
|
inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
|
|
{
|
|
for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
|
|
const int candidateIndex = component.namedObjectsInComponentTable()[i];
|
|
const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
|
|
if (candidate.id == id)
|
|
return candidateIndex;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#endif // QQMLPROPERTYCACHECREATOR_P_H
|