diff options
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 56 | ||||
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 195 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 19 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 24 |
7 files changed, 260 insertions, 44 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 669cdfbf3e..381f5bfec9 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -124,6 +124,8 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co this->pool = output->jsParserEngine.pool(); this->jsGenerator = &output->jsGenerator; + emptyStringIndex = registerString(QString()); + sourceCode = code; accept(program->imports); @@ -195,7 +197,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) bool isType = lastId->name.unicode()->isUpper(); if (isType) { int idx = defineQMLObject(node); - appendBinding(AST::SourceLocation(), registerString(QString()), idx); + appendBinding(AST::SourceLocation(), emptyStringIndex, idx); } else { int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer); appendBinding(node->qualifiedTypeNameId, idx); @@ -307,7 +309,7 @@ int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, _object->location.line = loc.startLine; _object->location.column = loc.startColumn; - _object->idIndex = registerString(QString()); + _object->idIndex = emptyStringIndex; _object->indexOfDefaultProperty = -1; _object->properties = New<PoolList<QmlProperty> >(); _object->qmlSignals = New<PoolList<Signal> >(); @@ -347,7 +349,7 @@ bool QQmlCodeGenerator::visit(AST::UiImport *node) uri = asString(node->importUri); } - import->qualifierIndex = registerString(QString()); + import->qualifierIndex = emptyStringIndex; // Qualifier if (!node->importId.isNull()) { @@ -416,6 +418,24 @@ bool QQmlCodeGenerator::visit(AST::UiImport *node) return false; } +static QStringList astNodeToStringList(QQmlJS::AST::Node *node) +{ + if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { + QString name = + static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString(); + return QStringList() << name; + } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { + QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node); + + QStringList rv = astNodeToStringList(expr->base); + if (rv.isEmpty()) + return rv; + rv.append(expr->name.toString()); + return rv; + } + return QStringList(); +} + bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) { static const struct TypeNameToType { @@ -507,7 +527,7 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) } else { // the parameter is a known basic type param->type = type->type; - param->customTypeNameIndex = registerString(QString()); + param->customTypeNameIndex = emptyStringIndex; } param->nameIndex = registerString(p->name.toString()); @@ -595,7 +615,7 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) if (type >= QV4::CompiledData::Property::Custom) property->customTypeNameIndex = registerString(memberType.toString()); else - property->customTypeNameIndex = registerString(QString()); + property->customTypeNameIndex = emptyStringIndex; property->nameIndex = registerString(name.toString()); @@ -603,7 +623,31 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) property->location.line = loc.startLine; property->location.column = loc.startColumn; - if (node->statement) + property->aliasPropertyValueIndex = emptyStringIndex; + + if (type == QV4::CompiledData::Property::Alias) { + if (!node->statement && !node->binding) + COMPILE_EXCEPTION(loc, tr("No property alias location")); + + QStringList alias; + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node->statement)) + alias = astNodeToStringList(stmt->expression); + + if (node->binding || alias.isEmpty()) + COMPILE_EXCEPTION(loc, tr("Invalid alias location")); + + if (alias.count() < 1 || alias.count() > 3) + COMPILE_EXCEPTION(loc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + + property->aliasIdValueIndex = registerString(alias.first()); + + QString propertyValue = alias.value(1); + if (alias.count() == 3) { + propertyValue += QLatin1Char('.'); + propertyValue += alias.at(2); + } + property->aliasPropertyValueIndex = registerString(propertyValue); + } else if (node->statement) appendBinding(node->identifierToken, property->nameIndex, node->statement); _object->properties->append(property); diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 981ac0a9a7..c3a3a8e4c3 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -262,6 +262,7 @@ public: QString sourceCode; QUrl url; QV4::Compiler::JSUnitGenerator *jsGenerator; + int emptyStringIndex; bool sanityCheckFunctionNames(); }; @@ -291,7 +292,7 @@ struct PropertyResolver return cache->property(index); } - QQmlPropertyData *property(const QString &name, bool *notInRevision); + QQmlPropertyData *property(const QString &name, bool *notInRevision = 0); // This code must match the semantics of QQmlPropertyPrivate::findSignalByName QQmlPropertyData *signal(const QString &name, bool *notInRevision); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 774cd70608..8481e17857 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -329,7 +329,11 @@ struct Property quint32 nameIndex; quint32 type; - quint32 customTypeNameIndex; + union { + quint32 customTypeNameIndex; // If type >= Custom + quint32 aliasIdValueIndex; // If type == Alias + }; + quint32 aliasPropertyValueIndex; quint32 flags; // readonly Location location; }; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 4e6c82c198..b4201fd10a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -54,6 +54,7 @@ #include <private/qqmlcomponentattached_p.h> #include <QQmlComponent> #include <private/qqmlcomponent_p.h> +#include <private/qqmlcodegenerator_p.h> QT_USE_NAMESPACE @@ -526,6 +527,8 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) Q_ASSERT(ddata); ddata->compiledData = compiledData; ddata->compiledData->addref(); + + QQmlEnginePrivate::get(engine)->registerInternalCompositeType(compiledData); } return instance; } @@ -1301,17 +1304,23 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi } -QQmlAnonymousComponentResolver::QQmlAnonymousComponentResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, +QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches) + const QList<QQmlPropertyCache *> &propertyCaches, QList<QByteArray> *vmeMetaObjectData, + QHash<int, int> *objectIndexToIdForRoot, + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent) : QQmlCompilePass(url, qmlUnit) , _componentIndex(-1) + , _objectIndexToIdInScope(0) , resolvedTypes(resolvedTypes) , propertyCaches(propertyCaches) + , vmeMetaObjectData(vmeMetaObjectData) + , objectIndexToIdForRoot(objectIndexToIdForRoot) + , objectIndexToIdPerComponent(objectIndexToIdPerComponent) { } -bool QQmlAnonymousComponentResolver::resolve() +bool QQmlComponentAndAliasResolver::resolve() { Q_ASSERT(componentRoots.isEmpty()); @@ -1357,32 +1366,58 @@ bool QQmlAnonymousComponentResolver::resolve() COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); _componentIndex = i; - _ids.clear(); - if (!recordComponentSubTree(rootBinding->value.objectIndex)) - break; + _idToObjectIndex.clear(); + + _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)]; + + _objectsWithAliases.clear(); + + if (!collectIdsAndAliases(rootBinding->value.objectIndex)) + return false; + + if (!resolveAliases()) + return false; } + // Collect ids and aliases for root + _componentIndex = -1; + _idToObjectIndex.clear(); + _objectIndexToIdInScope = objectIndexToIdForRoot; + _objectsWithAliases.clear(); + + collectIdsAndAliases(qmlUnit->indexOfRootObject); + + resolveAliases(); + return errors.isEmpty(); } -bool QQmlAnonymousComponentResolver::recordComponentSubTree(int objectIndex) +bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); // Only include creatable types. Everything else is synthetic, such as group property // objects. - if (!stringAt(obj->inheritedTypeNameIndex).isEmpty()) + if (_componentIndex != -1 && !stringAt(obj->inheritedTypeNameIndex).isEmpty()) objectIndexToComponentIndex.insert(objectIndex, _componentIndex); QString id = stringAt(obj->idIndex); if (!id.isEmpty()) { - if (_ids.contains(obj->idIndex)) { + if (_idToObjectIndex.contains(obj->idIndex)) { recordError(obj->locationOfIdProperty, tr("id is not unique")); return false; } - _ids.insert(obj->idIndex); + _idToObjectIndex.insert(obj->idIndex, objectIndex); + _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count()); } + const QV4::CompiledData::Property *property = obj->propertyTable(); + for (int i = 0; i < obj->nProperties; ++i, ++property) + if (property->type == QV4::CompiledData::Property::Alias) { + _objectsWithAliases.append(objectIndex); + break; + } + const QV4::CompiledData::Binding *binding = obj->bindingTable(); for (int i = 0; i < obj->nBindings; ++i, ++binding) { if (binding->type != QV4::CompiledData::Binding::Type_Object @@ -1394,9 +1429,147 @@ bool QQmlAnonymousComponentResolver::recordComponentSubTree(int objectIndex) if (std::binary_search(componentRoots.constBegin(), componentRoots.constEnd(), binding->value.objectIndex)) continue; - if (!recordComponentSubTree(binding->value.objectIndex)) + if (!collectIdsAndAliases(binding->value.objectIndex)) return false; } return true; } + +bool QQmlComponentAndAliasResolver::resolveAliases() +{ + foreach (int objectIndex, _objectsWithAliases) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); + + QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex); + Q_ASSERT(propertyCache); + + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectiveAliasIndex = 0; + + const QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 propertyIndex = 0; propertyIndex < obj->nProperties; ++propertyIndex, ++p) { + if (p->type != QV4::CompiledData::Property::Alias) + continue; + + const int idIndex = p->aliasIdValueIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) + COMPILE_EXCEPTION(p, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + const int targetId = _objectIndexToIdInScope->value(targetObjectIndex, -1); + Q_ASSERT(targetId != -1); + + const QString aliasPropertyValue = stringAt(p->aliasPropertyValueIndex); + + QStringRef property; + QStringRef subProperty; + + const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); + if (propertySeparator != -1) { + property = aliasPropertyValue.leftRef(propertySeparator); + subProperty = aliasPropertyValue.midRef(propertySeparator + 1); + } else + property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); + + int propIdx = -1; + int propType = 0; + int notifySignal = -1; + int flags = 0; + int type = 0; + bool writable = false; + bool resettable = false; + + quint32 propertyFlags = QQmlPropertyData::IsAlias; + + if (property.isEmpty()) { + const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(targetObjectIndex); + QQmlCompiledData::TypeReference typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex); + + if (typeRef.type) + type = typeRef.type->typeId(); + else + type = typeRef.component->metaTypeId; + + flags |= QML_ALIAS_FLAG_PTR; + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + } else { + QQmlPropertyCache *targetCache = propertyCaches.value(targetObjectIndex); + Q_ASSERT(targetCache); + QtQml::PropertyResolver resolver(targetCache); + + QQmlPropertyData *targetProperty = resolver.property(property.toString()); + if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + + propIdx = targetProperty->coreIndex; + type = targetProperty->propType; + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + notifySignal = targetProperty->notifyIndex; + + if (!subProperty.isEmpty()) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); + if (!valueType) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + + propType = type; + + int valueTypeIndex = + valueType->metaObject()->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + + propIdx |= (valueTypeIndex << 16); + if (valueType->metaObject()->property(valueTypeIndex).isEnumType()) + type = QVariant::Int; + else + type = valueType->metaObject()->property(valueTypeIndex).userType(); + + } else { + if (targetProperty->isEnum()) { + type = QVariant::Int; + } else { + // Copy type flags + propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + + if (targetProperty->isVarProperty()) + propertyFlags |= QQmlPropertyData::IsQVariant; + + if (targetProperty->isQObject()) + flags |= QML_ALIAS_FLAG_PTR; + } + } + } + + QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal }; + + typedef QQmlVMEMetaData VMD; + QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex]; + Q_ASSERT(!dynamicData.isEmpty()); + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + *(vmd->aliasData() + effectiveAliasIndex++) = aliasData; + + Q_ASSERT(dynamicData.isDetached()); + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable) + propertyFlags |= QQmlPropertyData::IsWritable; + else + propertyFlags &= ~QQmlPropertyData::IsWritable; + + if (resettable) + propertyFlags |= QQmlPropertyData::IsResettable; + else + propertyFlags &= ~QQmlPropertyData::IsResettable; + + QString propertyName = stringAt(p->nameIndex); + if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName; + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + + } + } + return true; +} diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 4b29295f0a..fe3a4bb00a 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -80,13 +80,16 @@ protected: QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; }; -class QQmlAnonymousComponentResolver : public QQmlCompilePass +class QQmlComponentAndAliasResolver : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) public: - QQmlAnonymousComponentResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, + QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches); + const QList<QQmlPropertyCache *> &propertyCaches, + QList<QByteArray> *vmeMetaObjectData, + QHash<int, int> *objectIndexToIdForRoot, + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent); bool resolve(); @@ -94,13 +97,19 @@ public: QHash<int, int> objectIndexToComponentIndex; protected: - bool recordComponentSubTree(int objectIndex); + bool collectIdsAndAliases(int objectIndex); + bool resolveAliases(); int _componentIndex; - QSet<int> _ids; + QHash<int, int> _idToObjectIndex; + QHash<int, int> *_objectIndexToIdInScope; + QList<int> _objectsWithAliases; const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; const QList<QQmlPropertyCache *> propertyCaches; + QList<QByteArray> *vmeMetaObjectData; + QHash<int, int> *objectIndexToIdForRoot; + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; }; class QmlObjectCreator : public QQmlCompilePass diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 32c0018819..bc0cef9f4c 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -341,6 +341,7 @@ private: friend class QQmlEnginePrivate; friend class QQmlCompiler; friend class QQmlPropertyCacheCreator; + friend class QQmlComponentAndAliasResolver; inline QQmlPropertyCache *copy(int reserve); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 096356126d..7b1935a447 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2281,28 +2281,12 @@ void QQmlTypeData::compile() } } - { - // Scan for anonymous components and determine their scopes. - QQmlAnonymousComponentResolver resolver(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, m_compiledData->propertyCaches); + if (errors.isEmpty()) { + // Scan for components, determine their scopes and resolve aliases within the scope. + QQmlComponentAndAliasResolver resolver(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, m_compiledData->propertyCaches, + &m_compiledData->datas, &m_compiledData->objectIndexToIdForRoot, &m_compiledData->objectIndexToIdPerComponent); if (!resolver.resolve()) errors << resolver.errors; - - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - - QHash<int, int> *objectIndexToId = 0; - QHash<int, int>::ConstIterator componentIt = resolver.objectIndexToComponentIndex.find(i); - if (componentIt != resolver.objectIndexToComponentIndex.constEnd()) { - int indexOfComponent = resolver.componentRoots[*componentIt]; - objectIndexToId = &m_compiledData->objectIndexToIdPerComponent[indexOfComponent]; - } else - objectIndexToId = &m_compiledData->objectIndexToIdForRoot; - - const QString &id = qmlUnit->header.stringAt(obj->idIndex); - if (id.isEmpty()) - continue; - objectIndexToId->insert(i, objectIndexToId->count()); - } } if (!errors.isEmpty()) { |