From a6c0a864751eb017da63317a67bc61541fddb480 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 13 Jun 2016 10:14:33 +0200 Subject: File naming cleanup Separate out the property cache creation code into a standalone file. Change-Id: Ib67bb1ef72c3de70ebd1ca8cae41947cbad7bfe3 Reviewed-by: Frank Meerkoetter --- src/qml/compiler/qqmlpropertycachecreator.cpp | 495 ++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/qml/compiler/qqmlpropertycachecreator.cpp (limited to 'src/qml/compiler/qqmlpropertycachecreator.cpp') diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp new file mode 100644 index 0000000000..1b886d5efb --- /dev/null +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qqmlpropertycachecreator_p.h" + +#include + +#define COMPILE_EXCEPTION(token, desc) \ + { \ + recordError((token)->location, desc); \ + return false; \ + } + +QT_BEGIN_NAMESPACE + +static QAtomicInt classIndexCounter(0); + +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , qmlObjects(*typeCompiler->qmlObjects()) + , imports(typeCompiler->imports()) + , resolvedTypes(typeCompiler->resolvedTypes()) +{ +} + +QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() +{ + for (int i = 0; i < propertyCaches.count(); ++i) + if (QQmlPropertyCache *cache = propertyCaches.at(i).data()) + cache->release(); + propertyCaches.clear(); +} + +bool QQmlPropertyCacheCreator::buildMetaObjects() +{ + propertyCaches.resize(qmlObjects.count()); + + if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) + return false; + + compiler->setPropertyCaches(propertyCaches); + propertyCaches.clear(); + + return true; +} + +bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + QQmlPropertyCache *baseTypeCache = 0; + QQmlPropertyData *instantiatingProperty = 0; + if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + Q_ASSERT(referencingObjectIndex >= 0); + QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex).data(); + Q_ASSERT(parentCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); + if (instantiatingProperty) { + if (instantiatingProperty->isQObject()) { + baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); + Q_ASSERT(baseTypeCache); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) { + baseTypeCache = enginePrivate->cache(vtmo); + Q_ASSERT(baseTypeCache); + } + } + } + + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; + if (!needVMEMetaObject) { + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + + // 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 (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { + needVMEMetaObject = false; + if (!ensureVMEMetaObject(referencingObjectIndex)) + return false; + } + break; + } + } + } + + if (obj->inheritedTypeNameIndex != 0) { + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->isFullyDynamicType) { + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { + recordError(obj->location, tr("Fully dynamic types cannot declare new properties.")); + return false; + } + if (obj->signalCount() > 0) { + recordError(obj->location, tr("Fully dynamic types cannot declare new signals.")); + return false; + } + if (obj->functionCount() > 0) { + recordError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); + return false; + } + } + + baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + Q_ASSERT(baseTypeCache); + } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { + auto *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); + Q_ASSERT(typeRef); + QQmlType *qmltype = typeRef->type; + if (!qmltype) { + QString propertyName = stringAt(instantiatingBinding->propertyNameIndex); + if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; + if (!attachedMo) { + recordError(instantiatingBinding->location, tr("Non-existent attached object")); + return false; + } + baseTypeCache = enginePrivate->cache(attachedMo); + Q_ASSERT(baseTypeCache); + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + if (!createMetaObject(objectIndex, obj, baseTypeCache)) + return false; + } else { + if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) + oldCache->release(); + propertyCaches[objectIndex] = baseTypeCache; + baseTypeCache->addref(); + } + } + + if (propertyCaches.at(objectIndex).data()) { + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) + return false; + } + } + + return true; +} + +bool QQmlPropertyCacheCreator::ensureVMEMetaObject(int objectIndex) +{ + const bool willCreateVMEMetaObject = propertyCaches.at(objectIndex).flag(); + if (willCreateVMEMetaObject) + return true; + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + auto *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + return createMetaObject(objectIndex, obj, baseTypeCache); +} + +bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) +{ + QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount()); + + if (QQmlPropertyCache *oldCache = propertyCaches.at(objectIndex).data()) + oldCache->release(); + propertyCaches[objectIndex] = cache; + // Indicate that this object also needs a VME meta-object at run-time + propertyCaches[objectIndex].setFlag(); + + 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 == compiler->rootObjectIndex()) { + QString path = compiler->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).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int varPropCount = 0; + + QmlIR::PropertyResolver resolver(baseTypeCache); + + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + } + + for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + if (d && d->isFinal()) + COMPILE_EXCEPTION(a, 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 seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + 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 + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + for (const QmlIR::Alias *a = obj->firstAlias(); a; a = a->next) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + // Dynamic signals + for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) { + const int paramCount = s->parameters->count; + + QList names; + names.reserve(paramCount); + QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + QmlIR::SignalParameter *param = s->parameters->first; + for (int i = 0; i < paramCount; ++i, param = param->next) { + 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 = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + paramTypes[i + 1] = compilationUnit->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) + COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { + QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; + + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + if (astFunction->formals) + flags |= QQmlPropertyData::HasArguments; + + QString slotName = astFunction->name.toString(); + if (seenSignals.contains(slotName)) + COMPILE_EXCEPTION(s, 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 parameterNames; + QQmlJS::AST::FormalParameterList *param = astFunction->formals; + while (param) { + parameterNames << param->name.toUtf8(); + param = param->next; + } + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { + int propertyType = 0; + quint32 propertyFlags = 0; + + if (p->type == QV4::CompiledData::Property::Var) { + propertyType = QMetaType::QVariant; + propertyFlags = QQmlPropertyData::IsVarProperty; + } else if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { + COMPILE_EXCEPTION(p, tr("Invalid property type")); + } + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + 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; + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + } else { + propertyType = qmltype->qListTypeId(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + return true; +} + +QT_END_NAMESPACE -- cgit v1.2.3