This implements support for "font.pixelSize: 24" for example. The representation
in the compile data structure is so that font.pixelSize is short-hand for
font {
pixelSize: 24
}
which means that inside the braces is a complete object initializer. For that
initializer we create a dedicated CompiledData::Object, which however has its
type name empty. When populating the outer instance then, the "font" property
is read as QQmlValueType (a QObject) and instead of creating a new QObject we
use that value type as instance to run the rest of the QML object initializer
(everything in braces).
Change-Id: Ic0a37ac77ab88f582546b9c09a3d06a07726420b
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node)
{
- int idx = defineQMLObject(node);
- appendBinding(AST::SourceLocation(), registerString(QString()), idx);
+ // The grammar can't distinguish between two different definitions here:
+ // Item { ... }
+ // versus
+ // font { ... }
+ // The former is a new binding with no property name and "Item" as type name,
+ // and the latter is a binding to the font property with no type name but
+ // only initializer.
+
+ AST::UiQualifiedId *lastId = node->qualifiedTypeNameId;
+ while (lastId->next)
+ lastId = lastId->next;
+ bool isType = lastId->name.unicode()->isUpper();
+ if (isType) {
+ int idx = defineQMLObject(node);
+ appendBinding(AST::SourceLocation(), registerString(QString()), idx);
+ } else {
+ int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer);
+ appendBinding(node->qualifiedTypeNameId, idx);
+ }
return false;
}
bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node)
{
int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer);
- appendBinding(node->qualifiedId->identifierToken, registerString(asString(node->qualifiedId)), idx);
+ appendBinding(node->qualifiedId, idx);
return false;
}
bool QQmlCodeGenerator::visit(AST::UiScriptBinding *node)
{
- appendBinding(node->qualifiedId->identifierToken, registerString(asString(node->qualifiedId)), node->statement);
+ appendBinding(node->qualifiedId, node->statement);
return false;
}
_object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId));
- AST::SourceLocation loc = qualifiedTypeNameId->firstSourceLocation();
+ AST::SourceLocation loc;
+ if (qualifiedTypeNameId)
+ loc = qualifiedTypeNameId->firstSourceLocation();
_object->location.line = loc.startLine;
_object->location.column = loc.startColumn;
}
}
+void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement *value)
+{
+ QmlObject *object = 0;
+ name = resolveQualifiedId(name, &object);
+ qSwap(_object, object);
+ appendBinding(name->identifierToken, registerString(name->name.toString()), value);
+ qSwap(_object, object);
+}
+
+void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex)
+{
+ QmlObject *object = 0;
+ name = resolveQualifiedId(name, &object);
+ qSwap(_object, object);
+ appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex);
+ qSwap(_object, object);
+}
+
void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value)
{
if (!sanityCheckPropertyName(nameLocation, propertyNameIndex))
_object->bindings->append(binding);
}
+AST::UiQualifiedId *QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId *name, QmlObject **object)
+{
+ *object = _object;
+ while (name->next) {
+ Binding *binding = New<Binding>();
+ binding->propertyNameIndex = registerString(name->name.toString());
+ binding->value.type = QV4::CompiledData::Value::Type_Object;
+
+ int objIndex = defineQMLObject(0, 0);
+ binding->value.objectIndex = objIndex;
+
+ (*object)->bindings->append(binding);
+ *object = _objects[objIndex];
+
+ name = name->next;
+ }
+ return name;
+}
+
bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex)
{
- QString name = jsGenerator->strings.at(nameIndex);
+ const QString &name = jsGenerator->strings.at(nameIndex);
+ if (name.isEmpty())
+ return true;
+
if (_propertyNames.contains(name))
COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name"));
void QQmlCodeGenerator::collectTypeReferences()
{
foreach (QmlObject *obj, _objects) {
- _typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
+ if (!stringAt(obj->inheritedTypeNameIndex).isEmpty())
+ _typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) {
if (prop->type >= QV4::CompiledData::Property::Custom)
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(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value);
void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex);
+ // resolves qualified name (font.pixelSize for example) and returns the last name along
+ // with the object any right-hand-side of a binding should apply to.
+ AST::UiQualifiedId *resolveQualifiedId(AST::UiQualifiedId *name, QmlObject **object);
+
bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex);
void recordError(const AST::SourceLocation &location, const QString &description);
int registerString(const QString &str) const { return jsGenerator->registerString(str); }
template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
+ QString stringAt(int index) const { return jsGenerator->strings.at(index); }
+
QList<QQmlError> errors;
QList<QV4::CompiledData::Import*> _imports;
struct Object
{
+ // An empty inherited type name suggests that this object doesn't require to be instantiated
+ // by itself but is merely used for grouped properties. It can therefore only have bindings,
+ // so nProperties, nFunctions and nSignals must be zero.
quint32 inheritedTypeNameIndex;
quint32 idIndex;
quint32 indexOfDefaultProperty;
}
for (int ii = 0; ii < propertyCaches.count(); ++ii)
- propertyCaches.at(ii)->release();
+ if (propertyCaches.at(ii))
+ propertyCaches.at(ii)->release();
for (int ii = 0; ii < scripts.count(); ++ii)
scripts.at(ii)->release();
bool QQmlPropertyCacheCreator::create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **resultCache, QByteArray *vmeMetaObjectData)
{
+ Q_ASSERT(!stringAt(obj->inheritedTypeNameIndex).isEmpty());
QQmlType *baseType = resolvedTypes->value(obj->inheritedTypeNameIndex).type;
Q_ASSERT(baseType);
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
QString name = stringAt(binding->propertyNameIndex);
+ // Child item:
+ // ...
+ // Item {
+ // ...
+ // }
if (name.isEmpty() && binding->value.type == QV4::CompiledData::Value::Type_Object) {
create(binding->value.objectIndex, _qobject);
continue;
QQmlPropertyData *property = _propertyCache->property(name, _qobject, context);
+ // Grouped property:
+ // ...
+ // font {
+ // pixelSize: 24
+ // ...
+ // }
+ if (binding->value.type == QV4::CompiledData::Value::Type_Object) {
+ const QV4::CompiledData::Object *obj = unit->objectAt(binding->value.objectIndex);
+ if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) {
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property->propType);
+
+ valueType->read(_qobject, property->coreIndex);
+
+ QQmlRefPointer<QQmlPropertyCache> cache = QQmlEnginePrivate::get(engine)->cache(valueType);
+ populateInstance(binding->value.objectIndex, valueType, cache);
+
+ valueType->write(_qobject, property->coreIndex, QQmlPropertyPrivate::BypassInterceptor);
+ continue;
+ }
+ }
+
if (_ddata->hasBindingBit(property->coreIndex))
removeBindingOnProperty(_qobject, property->coreIndex);
QQmlType *type = resolvedTypes.value(obj->inheritedTypeNameIndex).type;
Q_ASSERT(type);
- QObject *result = type->create();
+ QObject *instance = type->create();
// ### use no-event variant
if (parent)
- result->setParent(parent);
-
- QQmlData *declarativeData = QQmlData::get(result, /*create*/true);
+ instance->setParent(parent);
QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index);
Q_ASSERT(!cache.isNull());
+ context->addObject(instance);
+
+ populateInstance(index, instance, cache);
+
+ return instance;
+}
+
+void QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache)
+{
+ const QV4::CompiledData::Object *obj = unit->objectAt(index);
+
+ QQmlData *declarativeData = QQmlData::get(instance, /*create*/true);
+
qSwap(_propertyCache, cache);
- qSwap(_qobject, result);
+ qSwap(_qobject, instance);
qSwap(_compiledObject, obj);
qSwap(_ddata, declarativeData);
- context->addObject(_qobject);
-
const QByteArray data = vmeMetaObjectData.value(index);
if (!data.isEmpty()) {
// install on _object
qSwap(_propertyCache, cache);
qSwap(_ddata, declarativeData);
qSwap(_compiledObject, obj);
- qSwap(_qobject, result);
-
- return result;
+ qSwap(_qobject, instance);
}
QVariant QmlObjectCreator::variantForBinding(int expectedMetaType, const QV4::CompiledData::Binding *binding) const
QList<QQmlError> errors;
private:
+ void populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache);
+
QVector<QQmlAbstractBinding *> setupBindings(QV4::Object *qmlGlobal);
void setupFunctions(QV4::Object *qmlGlobal);
QByteArray vmeMetaObjectData;
QQmlPropertyCache *propertyCache = 0;
- if (!propertyCacheBuilder.create(obj, &propertyCache, &vmeMetaObjectData)) {
- errors << propertyCacheBuilder.errors;
- break;
+ // If the object has no type, then it's probably a nested object definition as part
+ // of a group property.
+ const bool objectHasType = !parsedQML->jsGenerator.strings.at(obj->inheritedTypeNameIndex).isEmpty();
+ if (objectHasType) {
+ if (!propertyCacheBuilder.create(obj, &propertyCache, &vmeMetaObjectData)) {
+ errors << propertyCacheBuilder.errors;
+ break;
+ }
}
- Q_ASSERT(propertyCache);
-
m_compiledData->datas << vmeMetaObjectData;
- propertyCache->addref();
+ if (propertyCache)
+ propertyCache->addref();
m_compiledData->propertyCaches << propertyCache;
}