diff options
author | Martin Jones <martin.jones@nokia.com> | 2009-05-27 07:01:31 (GMT) |
---|---|---|
committer | Martin Jones <martin.jones@nokia.com> | 2009-05-27 07:01:31 (GMT) |
commit | cc31d8c9de2dd6d8f0f60a00d8b0dc2576a8a829 (patch) | |
tree | 6350d6715738a5a5b21c9379294ae05d37bad895 /src/declarative | |
parent | ed5d5f6f04cda1f7aa3ea66be5fefc0a85429794 (diff) | |
parent | d53e4499f864719446abdb7fdeff433dbb3f7d98 (diff) | |
download | Qt-cc31d8c9de2dd6d8f0f60a00d8b0dc2576a8a829.zip Qt-cc31d8c9de2dd6d8f0f60a00d8b0dc2576a8a829.tar.gz Qt-cc31d8c9de2dd6d8f0f60a00d8b0dc2576a8a829.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Diffstat (limited to 'src/declarative')
-rw-r--r-- | src/declarative/qml/qmlcompiler.cpp | 545 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler_p.h | 14 | ||||
-rw-r--r-- | src/declarative/qml/qmlinstruction.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlinstruction_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlmetatype.cpp | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlvme.cpp | 7 |
6 files changed, 306 insertions, 264 deletions
diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 89a1774..cce8109 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -184,76 +184,118 @@ bool QmlCompiler::isValidId(const QString &val) } /*! - Returns true if property name \a name refers to an attached property, false - otherwise. + Returns true if \a name refers to an attached property, false otherwise. Attached property names are those that start with a capital letter. */ -bool QmlCompiler::isAttachedProperty(const QByteArray &name) +bool QmlCompiler::isAttachedPropertyName(const QByteArray &name) { return !name.isEmpty() && name.at(0) >= 'A' && name.at(0) <= 'Z'; } -QmlCompiler::StoreInstructionResult -QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, - QmlInstruction &instr, - const QMetaProperty &prop, - int coreIdx, int primitive, - const QString *string) +/*! + Returns true if \a name refers to a signal property, false otherwise. + + Signal property names are those that start with "on", followed by a capital + letter. +*/ +bool QmlCompiler::isSignalPropertyName(const QByteArray &name) { + return name.length() >= 3 && name.startsWith("on") && + 'A' <= name.at(2) && 'Z' >= name.at(2); +} + +#define COMPILE_EXCEPTION2(token, desc) \ + { \ + QString exceptionDescription; \ + QmlError error; \ + error.setUrl(output->url); \ + error.setLine(token->location.start.line); \ + error.setColumn(token->location.start.column); \ + QDebug d(&exceptionDescription); \ + d << desc; \ + error.setDescription(exceptionDescription.trimmed()); \ + exceptions << error; \ + return false; \ + } + +#define COMPILE_EXCEPTION(desc) \ + { \ + QString exceptionDescription; \ + QmlError error; \ + error.setUrl(output->url); \ + error.setLine(obj->location.start.line); \ + error.setColumn(obj->location.start.column); \ + QDebug d(&exceptionDescription); \ + d << desc; \ + error.setDescription(exceptionDescription.trimmed()); \ + exceptions << error; \ + return false; \ + } + +#define COMPILE_CHECK(a) \ + { \ + if (!a) return false; \ + } + +// Compile a simple assignment of v to prop into instr +bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, + const QMetaProperty &prop, + QmlParser::Value *v) +{ + QString string = v->value.asScript(); + if (!prop.isWritable()) - return ReadOnly; + COMPILE_EXCEPTION2(v, "Cannot assign literal value to read-only property" << prop.name()); + if (prop.isEnumType()) { int value; if (prop.isFlagType()) { - value = prop.enumerator().keysToValue(string->toLatin1().constData()); + value = prop.enumerator().keysToValue(string.toLatin1().constData()); } else - value = prop.enumerator().keyToValue(string->toLatin1().constData()); + value = prop.enumerator().keyToValue(string.toLatin1().constData()); if (value == -1) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot assign unknown enumeration to property" << prop.name()); instr.type = QmlInstruction::StoreInteger; - instr.storeInteger.propertyIndex = coreIdx; + instr.storeInteger.propertyIndex = prop.propertyIndex(); instr.storeInteger.value = value; - return Ok; + return true; } int type = prop.type(); switch(type) { case -1: + { instr.type = QmlInstruction::StoreVariant; - instr.storeString.propertyIndex = coreIdx; - if (primitive == -1) - primitive = cdata.indexForString(*string); - instr.storeString.value = primitive; - break; + instr.storeString.propertyIndex = prop.propertyIndex(); + instr.storeString.value = output->indexForString(string); + } break; case QVariant::String: { instr.type = QmlInstruction::StoreString; - instr.storeString.propertyIndex = coreIdx; - if (primitive == -1) - primitive = cdata.indexForString(*string); - instr.storeString.value = primitive; + instr.storeString.propertyIndex = prop.propertyIndex(); + instr.storeString.value = output->indexForString(string); } break; case QVariant::UInt: { instr.type = QmlInstruction::StoreInteger; - instr.storeInteger.propertyIndex = coreIdx; + instr.storeInteger.propertyIndex = prop.propertyIndex(); bool ok; - int value = string->toUInt(&ok); + int value = string.toUInt(&ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to unsigned integer"); instr.storeInteger.value = value; } break; case QVariant::Int: { instr.type = QmlInstruction::StoreInteger; - instr.storeInteger.propertyIndex = coreIdx; + instr.storeInteger.propertyIndex = prop.propertyIndex(); bool ok; - int value = string->toInt(&ok); + int value = string.toInt(&ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to integer"); instr.storeInteger.value = value; } break; @@ -261,59 +303,59 @@ QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, case QVariant::Double: { instr.type = QmlInstruction::StoreReal; - instr.storeReal.propertyIndex = coreIdx; + instr.storeReal.propertyIndex = prop.propertyIndex(); bool ok; - float value = string->toFloat(&ok); + float value = string.toFloat(&ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to real number"); instr.storeReal.value = value; } break; case QVariant::Color: { - QColor c = QmlStringConverters::colorFromString(*string); + QColor c = QmlStringConverters::colorFromString(string); if (!c.isValid()) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to color"); instr.type = QmlInstruction::StoreColor; - instr.storeColor.propertyIndex = coreIdx; + instr.storeColor.propertyIndex = prop.propertyIndex(); instr.storeColor.value = c.rgba(); } break; case QVariant::Date: { - QDate d = QDate::fromString(*string, Qt::ISODate); + QDate d = QDate::fromString(string, Qt::ISODate); if (!d.isValid()) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to date"); instr.type = QmlInstruction::StoreDate; - instr.storeDate.propertyIndex = coreIdx; + instr.storeDate.propertyIndex = prop.propertyIndex(); instr.storeDate.value = d.toJulianDay(); } break; case QVariant::Time: { - QTime time = QTime::fromString(*string, Qt::ISODate); + QTime time = QTime::fromString(string, Qt::ISODate); if (!time.isValid()) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to time"); int data[] = { time.hour(), time.minute(), time.second(), time.msec() }; - int index = cdata.indexForInt(data, 4); + int index = output->indexForInt(data, 4); instr.type = QmlInstruction::StoreTime; - instr.storeTime.propertyIndex = coreIdx; + instr.storeTime.propertyIndex = prop.propertyIndex(); instr.storeTime.valueIndex = index; } break; case QVariant::DateTime: { - QDateTime dateTime = QDateTime::fromString(*string, Qt::ISODate); + QDateTime dateTime = QDateTime::fromString(string, Qt::ISODate); if (!dateTime.isValid()) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to date and time"); int data[] = { dateTime.date().toJulianDay(), dateTime.time().hour(), dateTime.time().minute(), dateTime.time().second(), dateTime.time().msec() }; - int index = cdata.indexForInt(data, 5); + int index = output->indexForInt(data, 5); instr.type = QmlInstruction::StoreDateTime; - instr.storeDateTime.propertyIndex = coreIdx; + instr.storeDateTime.propertyIndex = prop.propertyIndex(); instr.storeDateTime.valueIndex = index; } break; @@ -321,16 +363,16 @@ QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, case QVariant::PointF: { bool ok; - QPointF point = QmlStringConverters::pointFFromString(*string, &ok); + QPointF point = QmlStringConverters::pointFFromString(string, &ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to point"); float data[] = { point.x(), point.y() }; - int index = cdata.indexForFloat(data, 2); + int index = output->indexForFloat(data, 2); if (type == QVariant::PointF) instr.type = QmlInstruction::StorePointF; else instr.type = QmlInstruction::StorePoint; - instr.storeRealPair.propertyIndex = coreIdx; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); instr.storeRealPair.valueIndex = index; } break; @@ -338,16 +380,16 @@ QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, case QVariant::SizeF: { bool ok; - QSizeF size = QmlStringConverters::sizeFFromString(*string, &ok); + QSizeF size = QmlStringConverters::sizeFFromString(string, &ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to size"); float data[] = { size.width(), size.height() }; - int index = cdata.indexForFloat(data, 2); + int index = output->indexForFloat(data, 2); if (type == QVariant::SizeF) instr.type = QmlInstruction::StoreSizeF; else instr.type = QmlInstruction::StoreSize; - instr.storeRealPair.propertyIndex = coreIdx; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); instr.storeRealPair.valueIndex = index; } break; @@ -355,28 +397,28 @@ QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, case QVariant::RectF: { bool ok; - QRectF rect = QmlStringConverters::rectFFromString(*string, &ok); + QRectF rect = QmlStringConverters::rectFFromString(string, &ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to rect"); float data[] = { rect.x(), rect.y(), rect.width(), rect.height() }; - int index = cdata.indexForFloat(data, 4); + int index = output->indexForFloat(data, 4); if (type == QVariant::RectF) instr.type = QmlInstruction::StoreRectF; else instr.type = QmlInstruction::StoreRect; - instr.storeRect.propertyIndex = coreIdx; + instr.storeRect.propertyIndex = prop.propertyIndex(); instr.storeRect.valueIndex = index; } break; case QVariant::Bool: { bool ok; - bool b = QmlStringConverters::boolFromString(*string, &ok); + bool b = QmlStringConverters::boolFromString(string, &ok); if (!ok) - return InvalidData; + COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to boolean"); instr.type = QmlInstruction::StoreBool; - instr.storeBool.propertyIndex = coreIdx; + instr.storeBool.propertyIndex = prop.propertyIndex(); instr.storeBool.value = b; } break; @@ -388,24 +430,22 @@ QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, QmlMetaType::StringConverter converter = QmlMetaType::customStringConverter(t); if (converter) { - int index = cdata.customTypeData.count(); + int index = output->customTypeData.count(); instr.type = QmlInstruction::AssignCustomType; - instr.assignCustomType.propertyIndex = coreIdx; + instr.assignCustomType.propertyIndex = prop.propertyIndex(); instr.assignCustomType.valueIndex = index; QmlCompiledData::CustomTypeData data; - if (primitive == -1) - primitive = cdata.indexForString(*string); - data.index = primitive; + data.index = output->indexForString(string); data.type = t; - cdata.customTypeData << data; + output->customTypeData << data; break; } } - return UnknownType; + COMPILE_EXCEPTION2(v, "Cannot assign to property" << prop.name() << "of unknown type" << prop.type()); break; } - return Ok; + return true; } void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) @@ -424,39 +464,6 @@ void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) cc->bytecode.clear(); } -#define COMPILE_EXCEPTION2(token, desc) \ - { \ - QString exceptionDescription; \ - QmlError error; \ - error.setUrl(output->url); \ - error.setLine(token->location.start.line); \ - error.setColumn(token->location.start.column); \ - QDebug d(&exceptionDescription); \ - d << desc; \ - error.setDescription(exceptionDescription.trimmed()); \ - exceptions << error; \ - return false; \ - } - -#define COMPILE_EXCEPTION(desc) \ - { \ - QString exceptionDescription; \ - QmlError error; \ - error.setUrl(output->url); \ - error.setLine(obj->location.start.line); \ - error.setColumn(obj->location.start.column); \ - QDebug d(&exceptionDescription); \ - d << desc; \ - error.setDescription(exceptionDescription.trimmed()); \ - exceptions << error; \ - return false; \ - } - -#define COMPILE_CHECK(a) \ - { \ - if (!a) return false; \ - } - bool QmlCompiler::compile(QmlEngine *engine, QmlCompositeTypeData *unit, QmlCompiledComponent *out) @@ -540,8 +547,8 @@ void QmlCompiler::compileTree(Object *tree) bool QmlCompiler::compileObject(Object *obj, int ctxt) { - if (obj->type != -1) - obj->metatype = output->types.at(obj->type).metaObject(); + Q_ASSERT (obj->type != -1); + obj->metatype = output->types.at(obj->type).metaObject(); if (output->types.at(obj->type).className == "Component") { COMPILE_CHECK(compileComponent(obj, ctxt)); @@ -559,23 +566,22 @@ bool QmlCompiler::compileObject(Object *obj, int ctxt) create.create.type = obj->type; output->bytecode << create; + // Create the synthesized meta object COMPILE_CHECK(compileDynamicMeta(obj)); - int parserStatusCast = -1; - if (obj->type != -1) { - // ### Optimize - const QMetaObject *mo = obj->metatype; - QmlType *type = 0; - while (!type && mo) { - type = QmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - - Q_ASSERT(type); - - parserStatusCast = type->parserStatusCast(); + // Find the native type and check for the QmlParserStatus interface + // ### Optimize + const QMetaObject *mo = obj->metatype; + QmlType *type = 0; + while (!type && mo) { + type = QmlMetaType::qmlType(mo); + mo = mo->superClass(); } + Q_ASSERT(type); + int parserStatusCast = type->parserStatusCast(); + // If the type support the QmlParserStatusInterface we need to invoke + // classBegin() if (parserStatusCast != -1) { QmlInstruction begin; begin.type = QmlInstruction::BeginObject; @@ -584,35 +590,50 @@ bool QmlCompiler::compileObject(Object *obj, int ctxt) output->bytecode << begin; } + // Check if this is a custom parser type. Custom parser types allow + // assignments to non-existant properties. These assignments are then + // compiled by the type. bool isCustomParser = output->types.at(obj->type).type && output->types.at(obj->type).type->customParser() != 0; QList<QmlCustomParserProperty> customProps; + // Compile all explicit properties specified foreach(Property *prop, obj->properties) { - if (prop->name.length() >= 3 && prop->name.startsWith("on") && - ('A' <= prop->name.at(2) && 'Z' >= prop->name.at(2))) { - if (!isCustomParser) { - COMPILE_CHECK(compileSignal(prop, obj)); + + if (isCustomParser) { + // Custom parser types don't support signal properties + if (testProperty(prop, obj)) { + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); } else { customProps << QmlCustomParserNodePrivate::fromProperty(prop); } } else { - if (!isCustomParser || (isCustomParser && testProperty(prop, obj))) { - COMPILE_CHECK(compileProperty(prop, obj, ctxt)); + if (isSignalPropertyName(prop->name)) { + COMPILE_CHECK(compileSignal(prop,obj)); } else { - customProps << QmlCustomParserNodePrivate::fromProperty(prop); + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); } } + } + // Compile the default property if (obj->defaultProperty) { - if(!isCustomParser || (isCustomParser && testProperty(obj->defaultProperty, obj))) { - COMPILE_CHECK(compileProperty(obj->defaultProperty, obj, ctxt)); + Property *prop = obj->defaultProperty; + + if (isCustomParser) { + if (testProperty(prop, obj)) { + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); + } else { + customProps << QmlCustomParserNodePrivate::fromProperty(prop); + } } else { - customProps << QmlCustomParserNodePrivate::fromProperty(obj->defaultProperty); + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); } + } + // Compile custom parser parts if (isCustomParser && !customProps.isEmpty()) { // ### Check for failure bool ok = false; @@ -625,6 +646,8 @@ bool QmlCompiler::compileObject(Object *obj, int ctxt) output->indexForByteArray(customData); } + // If the type support the QmlParserStatusInterface we need to invoke + // classComplete() if (parserStatusCast != -1) { QmlInstruction complete; complete.type = QmlInstruction::CompleteObject; @@ -717,12 +740,13 @@ bool QmlCompiler::compileComponentFromRoot(Object *obj, int ctxt) bool QmlCompiler::compileFetchedObject(Object *obj, int ctxt) { + Q_ASSERT(obj->metatype); + if (obj->defaultProperty) COMPILE_CHECK(compileProperty(obj->defaultProperty, obj, ctxt)); foreach(Property *prop, obj->properties) { - if (prop->name.length() >= 3 && prop->name.startsWith("on") && - ('A' <= prop->name.at(2) && 'Z' >= prop->name.at(2))) { + if (isSignalPropertyName(prop->name)) { COMPILE_CHECK(compileSignal(prop, obj)); } else { COMPILE_CHECK(compileProperty(prop, obj, ctxt)); @@ -815,7 +839,7 @@ bool QmlCompiler::compileSignal(Property *prop, Object *obj) bool QmlCompiler::testProperty(QmlParser::Property *prop, QmlParser::Object *obj) { - if(isAttachedProperty(prop->name) || prop->name == "id") + if(isAttachedPropertyName(prop->name) || prop->name == "id") return true; const QMetaObject *mo = obj->metaObject(); @@ -835,54 +859,71 @@ bool QmlCompiler::testProperty(QmlParser::Property *prop, bool QmlCompiler::compileProperty(Property *prop, Object *obj, int ctxt) { if (prop->values.isEmpty() && !prop->value) - return true; + COMPILE_EXCEPTION2(prop, "Empty property assignment"); - // First we're going to need a reference to this property - const QMetaObject *mo = obj->metaObject(); - if (mo && !isAttachedProperty(prop->name)) { - if (prop->isDefault) { - QMetaProperty p = QmlMetaType::defaultProperty(mo); - // XXX - // Currently we don't handle enums in the static analysis - // so we let them drop through to generateStoreInstruction() - if (p.name() && !p.isEnumType()) { - prop->index = mo->indexOfProperty(p.name()); - prop->name = p.name(); + const QMetaObject *metaObject = obj->metaObject(); + Q_ASSERT(metaObject); - int t = p.type(); - if (t == QVariant::UserType) - t = p.userType(); + if (isAttachedPropertyName(prop->name)) { + // Setup attached property data + QmlType *type = QmlMetaType::qmlType(prop->name); + + if (!type || !type->attachedPropertiesType()) + COMPILE_EXCEPTION2(prop, "Non-existant attached object"); - prop->type = t; + if (!prop->value) + COMPILE_EXCEPTION2(prop, "Cannot assign directly to attached object"); + + prop->value->metatype = type->attachedPropertiesType(); + } else { + // Setup regular property data + QMetaProperty p; + + if (prop->isDefault) { + p = QmlMetaType::defaultProperty(metaObject); + + if (p.name()) { + prop->index = p.propertyIndex(); + prop->name = p.name(); } + } else { - prop->index = mo->indexOfProperty(prop->name.constData()); - QMetaProperty p = mo->property(prop->index); - // XXX - // Currently we don't handle enums in the static analysis - // so we let them drop through to generateStoreInstruction() - if (p.name() && !p.isEnumType()) { - int t = p.type(); - if (t == QVariant::UserType) - t = p.userType(); + prop->index = metaObject->indexOfProperty(prop->name.constData()); - prop->type = t; + if (prop->index != -1) { + p = metaObject->property(prop->index); + Q_ASSERT(p.name()); } } - } else if(isAttachedProperty(prop->name) && prop->value) { - QmlType *type = QmlMetaType::qmlType(prop->name); - if (type && type->attachedPropertiesType()) - prop->value->metatype = type->attachedPropertiesType(); + + // We can't error here as the "id" property does not require a + // successful index resolution + if (p.name()) { + int t = p.type(); + + if (t == QVariant::UserType) + t = p.userType(); + + prop->type = t; + } } - if (prop->name == "id") { + if (!prop->isDefault && prop->name == "id") { COMPILE_CHECK(compileIdProperty(prop, obj)); - } else if (isAttachedProperty(prop->name)) { + } else if (isAttachedPropertyName(prop->name)) { COMPILE_CHECK(compileAttachedProperty(prop, obj, ctxt)); + } else if (prop->index == -1) { + + if (prop->isDefault) { + COMPILE_EXCEPTION2(prop, "Cannot assign to non-existant default property"); + } else { + COMPILE_EXCEPTION2(prop, "Cannot assign to non-existant property" << prop->name); + } + } else if (prop->value) { COMPILE_CHECK(compileNestedProperty(prop, ctxt)); @@ -948,19 +989,22 @@ bool QmlCompiler::compileIdProperty(QmlParser::Property *prop, return true; } +// Compile attached property object. In this example, +// Text { +// GridView.row: 10 +// } +// GridView is an attached property object. bool QmlCompiler::compileAttachedProperty(QmlParser::Property *prop, QmlParser::Object *obj, int ctxt) { - if (!prop->value) - COMPILE_EXCEPTION("Incorrect usage of an attached property"); + Q_ASSERT(prop->value); + int id = QmlMetaType::attachedPropertiesFuncId(prop->name); + Q_ASSERT(id != -1); // This is checked in compileProperty() QmlInstruction fetch; fetch.type = QmlInstruction::FetchAttached; fetch.line = prop->location.start.line; - int id = QmlMetaType::attachedPropertiesFuncId(prop->name); - if (id == -1) - COMPILE_EXCEPTION("Non-existant attached property object" << prop->name); fetch.fetchAttached.id = id; output->bytecode << fetch; @@ -974,16 +1018,20 @@ bool QmlCompiler::compileAttachedProperty(QmlParser::Property *prop, return true; } +// Compile "nested" properties. In this example: +// Text { +// font.size: 12 +// } +// font is a nested property. size is not. bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, int ctxt) { - if (prop->type != 0) - prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); - - if (prop->index == -1) - COMPILE_EXCEPTION2(prop, "Cannot access non-existant property" << prop->name); + Q_ASSERT(prop->type != 0); + Q_ASSERT(prop->index != -1); - if (!QmlMetaType::isObject(prop->value->metatype)) + // Load the nested property's meta type + prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); + if (!prop->value->metatype) COMPILE_EXCEPTION2(prop, "Cannot nest non-QObject property" << prop->name); QmlInstruction fetch; @@ -1003,11 +1051,20 @@ bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, return true; } +// Compile assignments to QML lists. QML lists are properties of type +// QList<T *> * and QmlList<T *> *. +// +// QList<T *> * types can accept a list of objects, or a single binding +// QmlList<T *> * types can accept a list of objects bool QmlCompiler::compileListProperty(QmlParser::Property *prop, QmlParser::Object *obj, int ctxt) { + Q_ASSERT(QmlMetaType::isList(prop->type) || + QmlMetaType::isQmlList(prop->type)); + int t = prop->type; + if (QmlMetaType::isQmlList(t)) { QmlInstruction fetch; fetch.line = prop->location.start.line; @@ -1037,8 +1094,6 @@ bool QmlCompiler::compileListProperty(QmlParser::Property *prop, pop.line = prop->location.start.line; output->bytecode << pop; } else { - Q_ASSERT(QmlMetaType::isList(t)); - QmlInstruction fetch; fetch.type = QmlInstruction::FetchQList; fetch.line = prop->location.start.line; @@ -1076,9 +1131,25 @@ bool QmlCompiler::compileListProperty(QmlParser::Property *prop, pop.type = QmlInstruction::PopQList; output->bytecode << pop; } + return true; } +// Compile regular property assignments of the form property: <value> +// +// ### The following problems exist +// +// There is no distinction between how "lists" of values are specified. This +// Item { +// children: Item {} +// children: Item {} +// } +// is identical to +// Item { +// children: [ Item {}, Item {} ] +// } +// +// We allow assignming multiple values to single value properties bool QmlCompiler::compilePropertyAssignment(QmlParser::Property *prop, QmlParser::Object *obj, int ctxt) @@ -1099,97 +1170,98 @@ bool QmlCompiler::compilePropertyAssignment(QmlParser::Property *prop, return true; } +// Compile assigning a single object instance to a regular property bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, QmlParser::Object *obj, QmlParser::Value *v, int ctxt) { - if (v->object->type != -1) - v->object->metatype = output->types.at(v->object->type).metaObject(); - Q_ASSERT(v->object->metaObject()); - - if (prop->index == -1) - COMPILE_EXCEPTION2(prop, "Cannot assign object to non-existant property" << prop->name); - + Q_ASSERT(prop->index != -1); + Q_ASSERT(v->object->type != -1); if (QmlMetaType::isInterface(prop->type)) { + // Assigning an object to an interface ptr property COMPILE_CHECK(compileObject(v->object, ctxt)); QmlInstruction assign; assign.type = QmlInstruction::StoreInterface; assign.line = v->object->location.start.line; assign.storeObject.propertyIndex = prop->index; - assign.storeObject.cast = 0; output->bytecode << assign; v->type = Value::CreatedObject; } else if (prop->type == -1) { - // Variant - // ### Is it always? + // Assigning an object to a QVariant COMPILE_CHECK(compileObject(v->object, ctxt)); QmlInstruction assign; assign.type = QmlInstruction::StoreVariantObject; assign.line = v->object->location.start.line; assign.storeObject.propertyIndex = prop->index; - assign.storeObject.cast = 0; output->bytecode << assign; v->type = Value::CreatedObject; } else { + // Normally compileObject() will set this up, but we need the static + // meta object earlier to test for assignability. It doesn't matter + // that there may still be outstanding synthesized meta object changes + // on this type, as they are not relevant for assignability testing + v->object->metatype = output->types.at(v->object->type).metaObject(); + Q_ASSERT(v->object->metaObject()); - const QMetaObject *propmo = + // We want to raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions that might + // effect the properties on the type, but don't effect assignability + const QMetaObject *propertyMetaObject = QmlMetaType::rawMetaObjectForType(prop->type); - bool isPropertyValue = false; + // Will be true if the assigned type inherits QmlPropertyValueSource + bool isPropertyValue = false; + // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; - - if (propmo) { - // We want to raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions + // Determine isPropertyValue and isAssignable values + if (propertyMetaObject) { const QMetaObject *c = v->object->metatype; - while(propmo && c) { - isPropertyValue = isPropertyValue || (c == &QmlPropertyValueSource::staticMetaObject); - isAssignable = isAssignable || (c == propmo); + while(c) { + isPropertyValue |= (c == &QmlPropertyValueSource::staticMetaObject); + isAssignable |= (c == propertyMetaObject); c = c->superClass(); } } else { const QMetaObject *c = v->object->metatype; while(!isPropertyValue && c) { - isPropertyValue = c == &QmlPropertyValueSource::staticMetaObject; + isPropertyValue |= (c == &QmlPropertyValueSource::staticMetaObject); c = c->superClass(); } } if (isAssignable) { + // Simple assignment COMPILE_CHECK(compileObject(v->object, ctxt)); QmlInstruction assign; assign.type = QmlInstruction::StoreObject; assign.line = v->object->location.start.line; assign.storeObject.propertyIndex = prop->index; - // XXX - this cast may not be 0 - assign.storeObject.cast = 0; output->bytecode << assign; v->type = Value::CreatedObject; - } else if (propmo == &QmlComponent::staticMetaObject) { - + } else if (propertyMetaObject == &QmlComponent::staticMetaObject) { + // Automatic "Component" insertion COMPILE_CHECK(compileComponentFromRoot(v->object, ctxt)); QmlInstruction assign; assign.type = QmlInstruction::StoreObject; assign.line = v->object->location.start.line; assign.storeObject.propertyIndex = prop->index; - // XXX - this cast may not be 0 - assign.storeObject.cast = 0; output->bytecode << assign; v->type = Value::Component; } else if (isPropertyValue) { + // Assign as a property value source COMPILE_CHECK(compileObject(v->object, ctxt)); QmlInstruction assign; @@ -1207,11 +1279,14 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, return true; } +// Compile assigning a literal or binding to a regular property bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, QmlParser::Object *obj, QmlParser::Value *v, int ctxt) { + Q_ASSERT(prop->index != -1); + if (v->value.isScript()) { COMPILE_CHECK(compileBinding(v->value.asScript(), prop, ctxt, @@ -1224,31 +1299,12 @@ bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, QmlInstruction assign; assign.line = v->location.start.line; - - if (prop->index != -1) { - QString value = v->primitive(); - StoreInstructionResult r = - generateStoreInstruction(*output, assign, obj->metaObject()->property(prop->index), prop->index, -1, &value); - - if (r == Ok) { - } else if (r == InvalidData) { - //### we are restricted to a rather generic message here. If we can find a way to move - // the exception into generateStoreInstruction we could potentially have better messages. - // (the problem is that both compile and run exceptions can be generated, though) - COMPILE_EXCEPTION2(v, "Cannot assign value" << v->primitive() << "to property" << obj->metaObject()->property(prop->index).name()); - } else if (r == ReadOnly) { - COMPILE_EXCEPTION2(v, "Cannot assign value" << v->primitive() << "to the read-only property" << obj->metaObject()->property(prop->index).name()); - } else { - COMPILE_EXCEPTION2(prop, "Cannot assign value to property" << obj->metaObject()->property(prop->index).name() << "of unknown type"); - } - } else { - COMPILE_EXCEPTION2(prop, "Cannot assign value to non-existant property" << prop->name); - } - + COMPILE_CHECK(compileStoreInstruction(assign, obj->metaObject()->property(prop->index), v)); output->bytecode << assign; v->type = Value::Literal; } + return true; } @@ -1343,6 +1399,9 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj) bool QmlCompiler::compileBinding(const QString &bind, QmlParser::Property *prop, int ctxt, const QMetaObject *mo, qint64 line) { + Q_ASSERT(mo); + Q_ASSERT(prop->index); + QmlBasicScript bs; bs.compile(bind.toLatin1()); @@ -1353,32 +1412,24 @@ bool QmlCompiler::compileBinding(const QString &bind, QmlParser::Property *prop, bref = output->indexForString(bind); } - if (prop->index != -1) { + QmlInstruction assign; + assign.assignBinding.context = ctxt; + assign.line = line; - QmlInstruction assign; - assign.assignBinding.context = ctxt; - assign.line = line; - - if (bs.isValid()) - assign.type = QmlInstruction::StoreCompiledBinding; - else - assign.type = QmlInstruction::StoreBinding; - - assign.assignBinding.property = prop->index; - assign.assignBinding.value = bref; - assign.assignBinding.category = QmlMetaProperty::Unknown; - if (mo) { - // ### we should generate an exception if the property is read-only - QMetaProperty mp = mo->property(assign.assignBinding.property); - assign.assignBinding.category = QmlMetaProperty::propertyCategory(mp); - } + if (bs.isValid()) + assign.type = QmlInstruction::StoreCompiledBinding; + else + assign.type = QmlInstruction::StoreBinding; - savedTypes.insert(output->bytecode.count(), prop->type); - output->bytecode << assign; + assign.assignBinding.property = prop->index; + assign.assignBinding.value = bref; + QMetaProperty mp = mo->property(assign.assignBinding.property); + if (!mp.isWritable() && !QmlMetaType::isList(prop->type)) + COMPILE_EXCEPTION2(prop, "Cannot assign binding to read-only property"); + assign.assignBinding.category = QmlMetaProperty::propertyCategory(mp); - } else { - COMPILE_EXCEPTION2(prop, "Cannot assign binding to non-existant property" << prop->name); - } + savedTypes.insert(output->bytecode.count(), prop->type); + output->bytecode << assign; return true; } diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 3280866..bc9e06c 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -119,16 +119,9 @@ public: QList<QmlError> errors() const; static bool isValidId(const QString &); - static bool isAttachedProperty(const QByteArray &); + static bool isAttachedPropertyName(const QByteArray &); + static bool isSignalPropertyName(const QByteArray &); - enum StoreInstructionResult { Ok, UnknownType, InvalidData, ReadOnly }; - static StoreInstructionResult - generateStoreInstruction(QmlCompiledData &data, - QmlInstruction &instr, - const QMetaProperty &prop, - int index, - int primitive, - const QString *string); private: void reset(QmlCompiledComponent *, bool); @@ -162,6 +155,9 @@ private: QmlParser::Object *obj, QmlParser::Value *value, int ctxt); + bool compileStoreInstruction(QmlInstruction &instr, + const QMetaProperty &prop, + QmlParser::Value *value); bool compileDynamicMeta(QmlParser::Object *obj); bool compileBinding(const QString &, QmlParser::Property *prop, diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index bb40063..0aa860d 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -116,7 +116,7 @@ void QmlCompiledComponent::dump(QmlInstruction *instr, int idx) qWarning() << idx << "\t" << line << "\t" << "STORE_VARIANT\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); break; case QmlInstruction::StoreObject: - qWarning() << idx << "\t" << line << "\t" << "STORE_OBJECT\t\t" << instr->storeObject.propertyIndex << "\t" << instr->storeObject.cast; + qWarning() << idx << "\t" << line << "\t" << "STORE_OBJECT\t\t" << instr->storeObject.propertyIndex; break; case QmlInstruction::AssignCustomType: qWarning() << idx << "\t" << line << "\t" << "ASSIGN_CUSTOMTYPE\t\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.valueIndex; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index abefd6c..5c6fa5a 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -235,7 +235,6 @@ public: } storeRect; struct { int propertyIndex; - int cast; } storeObject; struct { int propertyIndex; diff --git a/src/declarative/qml/qmlmetatype.cpp b/src/declarative/qml/qmlmetatype.cpp index 6d44f7a..3e25ae0 100644 --- a/src/declarative/qml/qmlmetatype.cpp +++ b/src/declarative/qml/qmlmetatype.cpp @@ -771,6 +771,7 @@ const char *QmlMetaType::interfaceIId(int userType) bool QmlMetaType::isObject(const QMetaObject *mo) { + // ### Huh? while(mo) { if (mo == &QObject::staticMetaObject) return true; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 991c7ad..e68afcc 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -546,8 +546,7 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in QObject *target = stack.top(); void *a[1]; - void *obj = (void *)(((char *)assignObj) + instr.storeObject.cast); - a[0] = (void *)&obj; + a[0] = (void *)&assignObj; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.storeObject.propertyIndex, a); @@ -674,8 +673,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in QmlMetaProperty mp(target, instr.assignBinding.property, (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); - if (!mp.isWritable()) - VME_EXCEPTION("Cannot assign a binding to read-only property" << mp.name()); QmlBindableValue *bind = new QmlBindableValue((void *)datas.at(instr.assignBinding.value).constData(), comp, context, 0); bindValues.append(bind); @@ -700,8 +697,6 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in QmlMetaProperty mp(target, instr.assignBinding.property, (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); - if (!mp.isWritable()) - VME_EXCEPTION("Cannot assign a binding to read-only property" << mp.name()); QmlBindableValue *bind = new QmlBindableValue(primitives.at(instr.assignBinding.value), context, false); bindValues.append(bind); |