aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-09-19 13:32:13 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-20 14:27:49 +0200
commit3123a22ae66016a0d9cd600dbb6f636fd29fc312 (patch)
treede483a714d4ac3234de4b13e3c6bb00d437d0a32 /src
parent4c953b1592956b712eca60eec070868a567c04c1 (diff)
[new compiler] Initial support for alias properties
Aliases are resolved at "compile time", not rough JS expressions. This is done right after we determined the scope of components in the QML file, which is also where we collect object ids (that aliases use). Change-Id: If5702337f2cca08d17f196c3b2fde3bbdfea5b3c Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/qml/compiler/qqmlcodegenerator.cpp56
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h3
-rw-r--r--src/qml/compiler/qv4compileddata_p.h6
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp195
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h19
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h1
-rw-r--r--src/qml/qml/qqmltypeloader.cpp24
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()) {