diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2013-09-30 17:39:49 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-17 08:10:07 +0100 |
commit | bf13e5045739f9766ed3c8ac1be1c672ce655dae (patch) | |
tree | 4506cbca3961567207f57c8eac92fdf705cfd29d /src | |
parent | 2b00cb4fc62f64b75c906a9f65cfc2b60ecfcb43 (diff) |
[new compiler] Add support for value interceptors / on-assignments
Behavior on x { NumberAnimation { ... } } is implemented by assigning a value
interceptor (Behavior is a sub-class of that) to the x property in a special
way. That requires various things:
* A VME meta-object must be created and installed on the surrounding object, in
order for the interceptors to work
* On assignments need to be excluded from duplicate property assignment checks
* Behaviours require also finalization callbacks on component creation
Change-Id: I40250b71081a2e315cda3bdb6677fa4b227fa443
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 17 | ||||
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator_p.h | 6 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 96 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 60 |
6 files changed, 150 insertions, 34 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 51881641cb..22ae1228c7 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -230,7 +230,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) { int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); - appendBinding(node->qualifiedId, idx); + appendBinding(node->qualifiedId, idx, node->hasOnToken); return false; } @@ -850,13 +850,13 @@ void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement * qSwap(_object, object); } -void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex) +void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment) { QmlObject *object = 0; if (!resolveQualifiedId(&name, &object)) return; qSwap(_object, object); - appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex); + appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment); qSwap(_object, object); } @@ -879,9 +879,9 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i _object->bindings->append(binding); } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem)) + if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem | isOnAssignment)) return; if (stringAt(propertyNameIndex) == QStringLiteral("id")) { @@ -901,6 +901,9 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i else binding->type = QV4::CompiledData::Binding::Type_Object; + if (isOnAssignment) + binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->value.objectIndex = objectIndex; _object->bindings->append(binding); } @@ -981,14 +984,14 @@ bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, Q return true; } -bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem) +bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItemOnOrAssignment) { const QString &name = jsGenerator->strings.at(nameIndex); if (name.isEmpty()) return true; // List items are implement by multiple bindings to the same name, so allow duplicates. - if (!isListItem) { + if (!isListItemOnOrAssignment) { if (_propertyNames.contains(name)) COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name")); diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 4b126ef5ea..f1050f4476 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -257,9 +257,9 @@ public: void setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement); void appendBinding(AST::UiQualifiedId *name, AST::Statement *value); - void appendBinding(AST::UiQualifiedId *name, int objectIndex); + void appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value); - void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false); + void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool setId(AST::Statement *value); @@ -267,7 +267,7 @@ public: // with the object any right-hand-side of a binding should apply to. bool resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object); - bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem = false); + bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItemOnOrAssignment = false); void recordError(const AST::SourceLocation &location, const QString &description); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 0c4ebcdf01..dd4f15d0e6 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -323,27 +323,8 @@ bool QQmlPropertyCacheCreator::buildMetaObjects() propertyCaches.resize(qmlObjects.count()); vmeMetaObjects.resize(qmlObjects.count()); - for (int i = 0; i < qmlObjects.count(); ++i) { - const QtQml::QmlObject *obj = qmlObjects.at(i); - - // If the object has no type, then it's probably a nested object definition as part - // of a group property. - const bool objectHasType = !stringAt(obj->inheritedTypeNameIndex).isEmpty(); - if (objectHasType) { - QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - QQmlPropertyCache *baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - Q_ASSERT(baseTypeCache); - - bool needVMEMetaObject = obj->properties->count != 0 || obj->qmlSignals->count != 0 || obj->functions->count != 0; - if (needVMEMetaObject) { - if (!createMetaObject(i, obj, baseTypeCache)) - return false; - } else { - propertyCaches[i] = baseTypeCache; - baseTypeCache->addref(); - } - } - } + if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) + return false; compiler->setVMEMetaObjects(vmeMetaObjects); compiler->setPropertyCaches(propertyCaches); @@ -352,6 +333,79 @@ bool QQmlPropertyCacheCreator::buildMetaObjects() return true; } +bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +{ + const QmlObject *obj = qmlObjects.at(objectIndex); + + QQmlPropertyCache *baseTypeCache = 0; + + bool needVMEMetaObject = obj->properties->count != 0 || obj->qmlSignals->count != 0 || obj->functions->count != 0; + if (!needVMEMetaObject) { + for (const QtQml::Binding *binding = obj->bindings->first; 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 (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex); + Q_ASSERT(parentCache); + Q_ASSERT(!stringAt(instantiatingBinding->propertyNameIndex).isEmpty()); + + bool notInRevision = false; + QQmlPropertyData *pd = PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); + Q_ASSERT(pd); + if (QQmlValueTypeFactory::isValueType(pd->propType)) { + needVMEMetaObject = false; + if (!ensureMetaObject(referencingObjectIndex)) + return false; + } else if (pd->isQObject()) { + baseTypeCache = enginePrivate->rawPropertyCacheForType(pd->propType); + Q_ASSERT(baseTypeCache); + } + } + break; + } + } + } + + QString typeName = stringAt(obj->inheritedTypeNameIndex); + if (!typeName.isEmpty()) { + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + Q_ASSERT(baseTypeCache); + } + + if (needVMEMetaObject) { + if (!createMetaObject(objectIndex, obj, baseTypeCache)) + return false; + } else if (baseTypeCache) { + propertyCaches[objectIndex] = baseTypeCache; + baseTypeCache->addref(); + } + + for (const QtQml::Binding *binding = obj->bindings->first; 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::ensureMetaObject(int objectIndex) +{ + if (!vmeMetaObjects.at(objectIndex).isEmpty()) + return true; + const QtQml::QmlObject *obj = qmlObjects.at(objectIndex); + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + QQmlPropertyCache *baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + return createMetaObject(objectIndex, obj, baseTypeCache); +} + bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache) { QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(QQmlEnginePrivate::get(enginePrivate), diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 7f7adf1c86..03cf3cafe7 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -125,6 +125,8 @@ public: bool buildMetaObjects(); protected: + bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); + bool ensureMetaObject(int objectIndex); bool createMetaObject(int objectIndex, const QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache); QQmlEnginePrivate *enginePrivate; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 3bffc5c8ef..f9e85ef080 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -280,7 +280,8 @@ struct Q_QML_EXPORT Binding }; enum Flags { - IsSignalHandlerExpression = 0x1 + IsSignalHandlerExpression = 0x1, + IsOnAssignment = 0x2 }; quint32 flags : 16; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 15f57f0761..963b8272f4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -54,6 +54,7 @@ #include <private/qqmlcomponent_p.h> #include <private/qqmlcustomparser_p.h> #include <private/qqmlscriptstring_p.h> +#include <private/qqmlpropertyvalueinterceptor_p.h> QT_USE_NAMESPACE @@ -670,7 +671,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI QObject *createdSubObject = 0; if (binding->type == QV4::CompiledData::Binding::Type_Object) { - createdSubObject = createInstance(binding->value.objectIndex, _qobject); + createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget); if (!createdSubObject) return false; } @@ -728,7 +729,8 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI } } - if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)) + if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) removeBindingOnProperty(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { @@ -767,6 +769,43 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI } if (binding->type == QV4::CompiledData::Binding::Type_Object) { + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { + // ### determine value source and interceptor casts ahead of time. + QQmlType *type = 0; + const QMetaObject *mo = createdSubObject->metaObject(); + while (mo && !type) { + type = QQmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + Q_ASSERT(type); + + QQmlPropertyData targetCorePropertyData = *property; + if (_valueTypeProperty) + targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + + int valueSourceCast = type->propertyValueSourceCast(); + if (valueSourceCast != -1) { + QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast); + QObject *target = createdSubObject->parent(); + vs->setTarget(QQmlPropertyPrivate::restore(target, targetCorePropertyData, context)); + return true; + } + int valueInterceptorCast = type->propertyValueInterceptorCast(); + if (valueInterceptorCast != -1) { + QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast); + QObject *target = createdSubObject->parent(); + + QQmlProperty prop = + QQmlPropertyPrivate::restore(target, targetCorePropertyData, context); + vi->setTarget(prop); + QQmlVMEMetaObject *mo = QQmlVMEMetaObject::get(target); + Q_ASSERT(mo); + mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi); + return true; + } + return false; + } + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; @@ -1032,6 +1071,23 @@ QQmlContextData *QmlObjectCreator::finalize() } { + QQmlTrace trace("VME Finalize Callbacks"); + for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) { + QQmlEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii); + QObject *obj = callback.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); + } +#if 0 // ### + if (watcher.hasRecursed()) + return 0; +#endif + } + finalizeCallbacks.clear(); + } + + { QQmlTrace trace("VME Component.onCompleted Callbacks"); while (componentAttached) { QQmlComponentAttached *a = componentAttached; |