diff options
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlcompiler_p.h | 7 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 20 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 229 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 38 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 28 |
9 files changed, 266 insertions, 62 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index f3a9b0213d..0802358d93 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -827,6 +827,8 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) #endif _object->idIndex = registerString(str.toString()); + _object->locationOfIdProperty.line = loc.startLine; + _object->locationOfIdProperty.column = loc.startColumn; return true; } @@ -993,6 +995,7 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output) objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty; objectToWrite->idIndex = o->idIndex; objectToWrite->location = o->location; + objectToWrite->locationOfIdProperty = o->locationOfIdProperty; quint32 nextOffset = sizeof(QV4::CompiledData::Object); diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 0fecd84684..981ac0a9a7 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -143,6 +143,7 @@ struct QmlObject int indexOfDefaultProperty; QV4::CompiledData::Location location; + QV4::CompiledData::Location locationOfIdProperty; PoolList<QmlProperty> *properties; PoolList<Signal> *qmlSignals; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 5f67d0c215..6d4a3b7548 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -351,6 +351,7 @@ struct Object quint32 nBindings; quint32 offsetToBindings; Location location; + Location locationOfIdProperty; // Function[] // Property[] // Signal[] diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 52d6fe210c..9e63ecb22c 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -140,8 +140,11 @@ public: // --- new compiler QV4::CompiledData::CompilationUnit *compilationUnit; QV4::CompiledData::QmlUnit *qmlUnit; - // ### sub-context support - QHash<int, int> objectIndexToId; + // index in first hash is component index, hash inside maps from object index in that scope to integer id + QHash<int, QHash<int, int> > objectIndexToIdPerComponent; + QHash<int, int> objectIndexToIdForRoot; + + bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } // --- struct Instruction { diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index a30d8becdb..3c8a50ad4f 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -900,24 +900,8 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) if (cc->compilationUnit && !cc->compilationUnit->engine) cc->compilationUnit->linkToEngine(v4); - // ### sub-contexts - QVector<QQmlContextData::ObjectIdMapping> mapping(cc->objectIndexToId.count()); - for (QHash<int, int>::ConstIterator it = cc->objectIndexToId.constBegin(), end = cc->objectIndexToId.constEnd(); - it != end; ++it) { - const QV4::CompiledData::Object *obj = cc->qmlUnit->objectAt(it.key()); - - QQmlContextData::ObjectIdMapping m; - m.id = it.value(); - m.name = cc->qmlUnit->header.stringAt(obj->idIndex); - mapping[m.id] = m; - } - context->setIdPropertyData(mapping); - - context->url = cc->url; - - state.creator = new QmlObjectCreator(context, cc->qmlUnit, cc->compilationUnit, cc->resolvedTypes, - cc->propertyCaches, cc->datas, cc->objectIndexToId); - rv = state.creator->create(); + state.creator = new QmlObjectCreator(context, cc); + rv = state.creator->create(start); if (!rv) state.errors = state.creator->errors; } else { diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 9877f59fb6..fe376d0e4a 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -129,6 +129,7 @@ private: Q_DISABLE_COPY(QQmlComponent) friend class QQmlVME; friend class QQmlTypeData; + friend class QmlObjectCreator; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index be96d473fa..03dfdb0e19 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -52,6 +52,8 @@ #include <private/qqmlboundsignal_p.h> #include <private/qqmltrace_p.h> #include <private/qqmlcomponentattached_p.h> +#include <QQmlComponent> +#include <private/qqmlcomponent_p.h> QT_USE_NAMESPACE @@ -462,20 +464,17 @@ static void removeBindingOnProperty(QObject *o, int index) if (binding) binding->destroy(); } -QmlObjectCreator::QmlObjectCreator(QQmlContextData *contextData, const QV4::CompiledData::QmlUnit *qmlUnit, - const QV4::CompiledData::CompilationUnit *jsUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache*> &propertyCaches, - const QList<QByteArray> &vmeMetaObjectData, const QHash<int, int> &objectIndexToId) - : QQmlCompilePass(contextData->url, qmlUnit) +QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData) + : QQmlCompilePass(compiledData->url, compiledData->qmlUnit) , componentAttached(0) - , engine(contextData->engine) - , jsUnit(jsUnit) - , context(contextData) - , resolvedTypes(resolvedTypes) - , propertyCaches(propertyCaches) - , vmeMetaObjectData(vmeMetaObjectData) - , objectIndexToId(objectIndexToId) + , engine(parentContext->engine) + , jsUnit(compiledData->compilationUnit) + , parentContext(parentContext) + , context(0) + , resolvedTypes(compiledData->resolvedTypes) + , propertyCaches(compiledData->propertyCaches) + , vmeMetaObjectData(compiledData->datas) + , compiledData(compiledData) , _qobject(0) , _compiledObject(0) , _ddata(0) @@ -485,6 +484,49 @@ QmlObjectCreator::QmlObjectCreator(QQmlContextData *contextData, const QV4::Comp { } +QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) +{ + int objectToCreate; + + if (subComponentIndex == -1) { + objectIndexToId = compiledData->objectIndexToIdForRoot; + objectToCreate = qmlUnit->indexOfRootObject; + } else { + objectIndexToId = compiledData->objectIndexToIdPerComponent[subComponentIndex]; + const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex); + objectToCreate = compObj->bindingTable()->value.objectIndex; + } + + context = new QQmlContextData; + context->isInternal = true; + context->url = compiledData->url; + context->urlString = compiledData->name; + context->imports = compiledData->importCache; + context->imports->addref(); + context->setParent(parentContext); + + QVector<QQmlContextData::ObjectIdMapping> mapping(objectIndexToId.count()); + for (QHash<int, int>::ConstIterator it = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); + it != end; ++it) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(it.key()); + + QQmlContextData::ObjectIdMapping m; + m.id = it.value(); + m.name = stringAt(obj->idIndex); + mapping[m.id] = m; + } + context->setIdPropertyData(mapping); + + QObject *instance = createInstance(objectToCreate, parent); + + QQmlData *ddata = QQmlData::get(instance); + Q_ASSERT(ddata); + ddata->compiledData = compiledData; + ddata->compiledData->addref(); + + return instance; +} + void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) { QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | @@ -932,7 +974,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI QObject *createdSubObject = 0; if (binding->type == QV4::CompiledData::Binding::Type_Object) { - createdSubObject = create(binding->value.objectIndex, _qobject); + createdSubObject = createInstance(binding->value.objectIndex, _qobject); if (!createdSubObject) return false; } @@ -1084,31 +1126,58 @@ void QmlObjectCreator::setupFunctions() } } -QObject *QmlObjectCreator::create(int index, QObject *parent) +QObject *QmlObjectCreator::createInstance(int index, QObject *parent) { ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + bool isComponent = false; + QObject *instance = 0; - QQmlType *type = resolvedTypes.value(obj->inheritedTypeNameIndex).type; - Q_ASSERT(type); + if (compiledData->isComponent(index)) { + isComponent = true; + QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent); + QQmlComponentPrivate::get(component)->creationContext = context; + instance = component; + } else { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - QObject *instance = type->create(); - // ### use no-event variant - if (parent) - instance->setParent(parent); + QQmlType *type = resolvedTypes.value(obj->inheritedTypeNameIndex).type; + Q_ASSERT(type); - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); - Q_ASSERT(!cache.isNull()); + instance = type->create(); + // ### use no-event variant + if (parent) + instance->setParent(parent); + } - context->addObject(instance); + QQmlData *ddata = QQmlData::get(instance, /*create*/true); + if (index == qmlUnit->indexOfRootObject) { + if (ddata->context) { + Q_ASSERT(ddata->context != context); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != context); + QQmlContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = context; + } else + context->addObject(instance); + ddata->ownContext = true; + } else if (!ddata->context) + context->addObject(instance); + + ddata->outerContext = context; QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(index); if (idEntry != objectIndexToId.constEnd()) context->setIdProperty(idEntry.value(), instance); - if (!populateInstance(index, instance, cache)) - return 0; + if (!isComponent) { + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); + Q_ASSERT(!cache.isNull()); + + if (!populateInstance(index, instance, cache)) + return 0; + } return instance; } @@ -1119,7 +1188,7 @@ void QmlObjectCreator::finalize() QQmlTrace trace("VME Binding Enable"); trace.event("begin binding eval"); - Q_ASSERT(allCreatedBindings.isDetached()); + Q_ASSERT(allCreatedBindings.isEmpty() || allCreatedBindings.isDetached()); for (QLinkedList<QVector<QQmlAbstractBinding*> >::Iterator it = allCreatedBindings.begin(), end = allCreatedBindings.end(); it != end; ++it) { @@ -1182,6 +1251,9 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi vmeMetaObject = QQmlVMEMetaObject::get(_qobject); } + _ddata->lineNumber = _compiledObject->location.line; + _ddata->columnNumber = _compiledObject->location.column; + qSwap(_vmeMetaObject, vmeMetaObject); QVector<QQmlAbstractBinding*> createdBindings(_compiledObject->nBindings, 0); @@ -1212,3 +1284,104 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi return errors.isEmpty(); } + + +QQmlAnonymousComponentResolver::QQmlAnonymousComponentResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, + const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, + const QList<QQmlPropertyCache *> &propertyCaches) + : QQmlCompilePass(url, qmlUnit) + , _componentIndex(-1) + , resolvedTypes(resolvedTypes) + , propertyCaches(propertyCaches) +{ +} + +bool QQmlAnonymousComponentResolver::resolve() +{ + Q_ASSERT(componentRoots.isEmpty()); + + // Find objects that are Components. This is missing an extra pass + // that finds implicitly defined components, i.e. + // someProperty: Item { ... } + // when someProperty _is_ a QQmlComponent. In that case the Item {} + // should be implicitly surrounded by Component {} + + for (int i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); + if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) + continue; + + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(i); + if (!cache || cache->metaObject() != &QQmlComponent::staticMetaObject) + continue; + + componentRoots.append(i); + // Sanity checks: There can be only an (optional) id property and + // a default property, that defines the component tree. + } + + std::sort(componentRoots.begin(), componentRoots.end()); + + // For each component's tree, remember to which component the children + // belong to + for (int i = 0; i < componentRoots.count(); ++i) { + const QV4::CompiledData::Object *component = qmlUnit->objectAt(componentRoots.at(i)); + + if (component->nFunctions > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new functions.")); + if (component->nProperties > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new properties.")); + if (component->nSignals > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new signals.")); + + if (component->nBindings == 0) + COMPILE_EXCEPTION(component, tr("Cannot create empty component specification")); + + const QV4::CompiledData::Binding *rootBinding = component->bindingTable(); + if (component->nBindings > 1 || rootBinding->type != QV4::CompiledData::Binding::Type_Object) + COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); + + _componentIndex = i; + _ids.clear(); + if (!recordComponentSubTree(rootBinding->value.objectIndex)) + break; + } + + return errors.isEmpty(); +} + +bool QQmlAnonymousComponentResolver::recordComponentSubTree(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()) + objectIndexToComponentIndex.insert(objectIndex, _componentIndex); + + QString id = stringAt(obj->idIndex); + if (!id.isEmpty()) { + if (_ids.contains(obj->idIndex)) { + recordError(obj->locationOfIdProperty, tr("id is not unique")); + return false; + } + _ids.insert(obj->idIndex); + } + + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (int i = 0; i < obj->nBindings; ++i, ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + // Stop at Component boundary + if (std::binary_search(componentRoots.constBegin(), componentRoots.constEnd(), binding->value.objectIndex)) + continue; + + if (!recordComponentSubTree(binding->value.objectIndex)) + return false; + } + + return true; +} diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index eb7a6233c5..e0aefaf81d 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -80,24 +80,44 @@ protected: QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; }; +class QQmlAnonymousComponentResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) +public: + QQmlAnonymousComponentResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, + const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, + const QList<QQmlPropertyCache *> &propertyCaches); + + bool resolve(); + + QVector<int> componentRoots; + QHash<int, int> objectIndexToComponentIndex; + +protected: + bool recordComponentSubTree(int objectIndex); + + int _componentIndex; + QSet<int> _ids; + + const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; + const QList<QQmlPropertyCache *> propertyCaches; +}; + class QmlObjectCreator : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator) public: - QmlObjectCreator(QQmlContextData *contextData, const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::CompilationUnit *jsUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, const QList<QQmlPropertyCache *> &propertyCaches, const QList<QByteArray> &vmeMetaObjectData, - const QHash<int, int> &objectIndexToId); - - QObject *create(QObject *parent = 0) - { return create(qmlUnit->indexOfRootObject, parent); } - QObject *create(int index, QObject *parent = 0); + QmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData); + QObject *create(int subComponentIndex, QObject *parent = 0); void finalize(); QQmlComponentAttached *componentAttached; QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; private: + QObject *createInstance(int index, QObject *parent = 0); + bool populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache); void setupBindings(); @@ -107,12 +127,14 @@ private: QQmlEngine *engine; const QV4::CompiledData::CompilationUnit *jsUnit; + QQmlContextData *parentContext; QQmlContextData *context; const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; const QList<QQmlPropertyCache *> propertyCaches; const QList<QByteArray> vmeMetaObjectData; - const QHash<int, int> &objectIndexToId; + QHash<int, int> objectIndexToId; QLinkedList<QVector<QQmlAbstractBinding*> > allCreatedBindings; + QQmlCompiledData *compiledData; QObject *_qobject; const QV4::CompiledData::Object *_compiledObject; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 8c8f8c9c25..7a54cab0f2 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2250,12 +2250,28 @@ void QQmlTypeData::compile() m_compiledData->propertyCaches << propertyCache; } - // ### support sub-contexts - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - const QString &id = qmlUnit->header.stringAt(obj->idIndex); - if (!id.isEmpty()) - m_compiledData->objectIndexToId.insert(i, m_compiledData->objectIndexToId.count()); + { + // Scan for anonymous components and determine their scopes. + QQmlAnonymousComponentResolver resolver(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, m_compiledData->propertyCaches); + 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()) { |