diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-07-14 03:54:55 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-07-15 07:30:16 (GMT) |
commit | 4f7e21dc04ce93531ef68f7694a5e8969448de8b (patch) | |
tree | ce7e8170b5be6c974420d012a03f620f86cd6d8d /src/declarative/qml/qmlcompiler.cpp | |
parent | 9eca9e028884fb82d97e284826faa7965af356bd (diff) | |
download | Qt-4f7e21dc04ce93531ef68f7694a5e8969448de8b.zip Qt-4f7e21dc04ce93531ef68f7694a5e8969448de8b.tar.gz Qt-4f7e21dc04ce93531ef68f7694a5e8969448de8b.tar.bz2 |
Rework compiler to a two phase analyse/generate approach
Diffstat (limited to 'src/declarative/qml/qmlcompiler.cpp')
-rw-r--r-- | src/declarative/qml/qmlcompiler.cpp | 1728 |
1 files changed, 921 insertions, 807 deletions
diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index b04c932..dafc581 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -71,90 +71,6 @@ QT_BEGIN_NAMESPACE using namespace QmlParser; -int QmlCompiledData::indexForString(const QString &data) -{ - int idx = primitives.indexOf(data); - if (idx == -1) { - idx = primitives.count(); - primitives << data; - } - return idx; -} - -int QmlCompiledData::indexForByteArray(const QByteArray &data) -{ - int idx = datas.indexOf(data); - if (idx == -1) { - idx = datas.count(); - datas << data; - } - return idx; -} - -int QmlCompiledData::indexForFloat(float *data, int count) -{ - Q_ASSERT(count > 0); - - for (int ii = 0; ii <= floatData.count() - count; ++ii) { - bool found = true; - for (int jj = 0; jj < count; ++jj) { - if (floatData.at(ii + jj) != data[jj]) { - found = false; - break; - } - } - - if (found) - return ii; - } - - int idx = floatData.count(); - for (int ii = 0; ii < count; ++ii) - floatData << data[ii]; - - return idx; -} - -int QmlCompiledData::indexForInt(int *data, int count) -{ - Q_ASSERT(count > 0); - - for (int ii = 0; ii <= intData.count() - count; ++ii) { - bool found = true; - for (int jj = 0; jj < count; ++jj) { - if (intData.at(ii + jj) != data[jj]) { - found = false; - break; - } - } - - if (found) - return ii; - } - - int idx = intData.count(); - for (int ii = 0; ii < count; ++ii) - intData << data[ii]; - - return idx; -} - -int QmlCompiledData::indexForLocation(const QmlParser::Location &l) -{ - // ### FIXME - int rv = locations.count(); - locations << l; - return rv; -} - -int QmlCompiledData::indexForLocation(const QmlParser::LocationSpan &l) -{ - // ### FIXME - int rv = locations.count(); - locations << l.start << l.end; - return rv; -} - QmlCompiler::QmlCompiler() : output(0) { @@ -241,14 +157,13 @@ bool QmlCompiler::isSignalPropertyName(const QByteArray &name) } // Compile a simple assignment of v to prop into instr -bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, - const QMetaProperty &prop, - QmlParser::Value *v) +bool QmlCompiler::testLiteralAssignment(const QMetaProperty &prop, + QmlParser::Value *v) { QString string = v->value.asScript(); if (!prop.isWritable()) - COMPILE_EXCEPTION2(v, "Cannot assign literal value to read-only property" << prop.name()); + COMPILE_EXCEPTION2(v, "Invalid property assignment: read-only property"); if (prop.isEnumType()) { int value; @@ -257,12 +172,136 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, } else value = prop.enumerator().keyToValue(string.toLatin1().constData()); if (value == -1) - COMPILE_EXCEPTION2(v, "Cannot assign unknown enumeration to property" << prop.name()); + COMPILE_EXCEPTION2(v, "Invalid property assignment: unknown enumeration"); + return true; + } + int type = prop.userType(); + switch(type) { + case -1: + break; + case QVariant::String: + if (!v->value.isString()) COMPILE_EXCEPTION2(v, "Invalid property assignment: string expected"); + break; + case QVariant::Url: + if (!v->value.isString()) COMPILE_EXCEPTION2(v, "Invalid property assignment: url expected"); + break; + case QVariant::UInt: + { + bool ok; + string.toUInt(&ok); + if (!v->value.isNumber() || !ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: unsigned int expected"); + } + break; + case QVariant::Int: + { + bool ok; + string.toInt(&ok); + if (!v->value.isNumber() || !ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: int expected"); + } + break; + case QMetaType::Float: + { + bool ok; + string.toFloat(&ok); + if (!v->value.isNumber() || !ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: float expected"); + } + break; + case QVariant::Double: + { + bool ok; + string.toDouble(&ok); + if (!v->value.isNumber() || !ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: double expected"); + } + break; + case QVariant::Color: + { + QColor c = QmlStringConverters::colorFromString(string); + if (!c.isValid()) COMPILE_EXCEPTION2(v, "Invalid property assignment: color expected"); + } + break; + case QVariant::Date: + { + QDate d = QDate::fromString(string, Qt::ISODate); + if (!d.isValid()) COMPILE_EXCEPTION2(v, "Invalid property assignment: date expected"); + } + break; + case QVariant::Time: + { + QTime time = QTime::fromString(string, Qt::ISODate); + if (!time.isValid()) COMPILE_EXCEPTION2(v, "Invalid property assignment: time expected"); + } + break; + case QVariant::DateTime: + { + QDateTime dateTime = QDateTime::fromString(string, Qt::ISODate); + if (!dateTime.isValid()) COMPILE_EXCEPTION2(v, "Invalid property assignment: datetime expected"); + } + break; + case QVariant::Point: + case QVariant::PointF: + { + bool ok; + QPointF point = QmlStringConverters::pointFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: point expected"); + } + break; + case QVariant::Size: + case QVariant::SizeF: + { + bool ok; + QSizeF size = QmlStringConverters::sizeFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: size expected"); + } + break; + case QVariant::Rect: + case QVariant::RectF: + { + bool ok; + QRectF rect = QmlStringConverters::rectFFromString(string, &ok); + if (!ok) COMPILE_EXCEPTION2(v, "Invalid property assignment: rect expected"); + } + break; + case QVariant::Bool: + { + if (!v->value.isBoolean()) COMPILE_EXCEPTION2(v, "Invalid property assignment: boolean expected"); + } + break; + default: + { + int t = prop.type(); + if (t == QVariant::UserType) + t = prop.userType(); + QmlMetaType::StringConverter converter = + QmlMetaType::customStringConverter(t); + if (!converter) + COMPILE_EXCEPTION2(v, "Invalid property assignment: unknown type" << prop.type()); + } + break; + } + return true; +} + +void QmlCompiler::genLiteralAssignment(const QMetaProperty &prop, + QmlParser::Value *v) +{ + QString string = v->value.asScript(); + + QmlInstruction instr; + instr.line = v->location.start.line; + if (prop.isEnumType()) { + int value; + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(string.toLatin1().constData()); + } else + value = prop.enumerator().keyToValue(string.toLatin1().constData()); + instr.type = QmlInstruction::StoreInteger; instr.storeInteger.propertyIndex = prop.propertyIndex(); instr.storeInteger.value = value; - return true; + output->bytecode << instr; + return; } + int type = prop.userType(); switch(type) { case -1: @@ -291,51 +330,33 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, { instr.type = QmlInstruction::StoreInteger; instr.storeInteger.propertyIndex = prop.propertyIndex(); - bool ok; - int value = string.toUInt(&ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to unsigned integer"); - instr.storeInteger.value = value; + instr.storeInteger.value = string.toUInt(); } break; case QVariant::Int: { instr.type = QmlInstruction::StoreInteger; instr.storeInteger.propertyIndex = prop.propertyIndex(); - bool ok; - int value = string.toInt(&ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to integer"); - instr.storeInteger.value = value; + instr.storeInteger.value = string.toInt(); } break; case QMetaType::Float: { instr.type = QmlInstruction::StoreFloat; instr.storeFloat.propertyIndex = prop.propertyIndex(); - bool ok; - float value = string.toFloat(&ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to float number"); - instr.storeFloat.value = value; + instr.storeFloat.value = string.toFloat(); } break; case QVariant::Double: { instr.type = QmlInstruction::StoreDouble; instr.storeDouble.propertyIndex = prop.propertyIndex(); - bool ok; - double value = string.toDouble(&ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to double number"); - instr.storeDouble.value = value; + instr.storeDouble.value = string.toDouble(); } break; case QVariant::Color: { QColor c = QmlStringConverters::colorFromString(string); - if (!c.isValid()) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to color"); instr.type = QmlInstruction::StoreColor; instr.storeColor.propertyIndex = prop.propertyIndex(); instr.storeColor.value = c.rgba(); @@ -344,8 +365,6 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, case QVariant::Date: { QDate d = QDate::fromString(string, Qt::ISODate); - if (!d.isValid()) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to date"); instr.type = QmlInstruction::StoreDate; instr.storeDate.propertyIndex = prop.propertyIndex(); instr.storeDate.value = d.toJulianDay(); @@ -354,9 +373,8 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, case QVariant::Time: { QTime time = QTime::fromString(string, Qt::ISODate); - if (!time.isValid()) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to time"); - int data[] = { time.hour(), time.minute(), time.second(), time.msec() }; + int data[] = { time.hour(), time.minute(), + time.second(), time.msec() }; int index = output->indexForInt(data, 4); instr.type = QmlInstruction::StoreTime; instr.storeTime.propertyIndex = prop.propertyIndex(); @@ -366,8 +384,6 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, case QVariant::DateTime: { QDateTime dateTime = QDateTime::fromString(string, Qt::ISODate); - if (!dateTime.isValid()) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to date and time"); int data[] = { dateTime.date().toJulianDay(), dateTime.time().hour(), dateTime.time().minute(), @@ -382,90 +398,76 @@ bool QmlCompiler::compileStoreInstruction(QmlInstruction &instr, case QVariant::Point: case QVariant::PointF: { - bool ok; - QPointF point = QmlStringConverters::pointFFromString(string, &ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to point"); - float data[] = { point.x(), point.y() }; - int index = output->indexForFloat(data, 2); - if (type == QVariant::PointF) - instr.type = QmlInstruction::StorePointF; - else - instr.type = QmlInstruction::StorePoint; - instr.storeRealPair.propertyIndex = prop.propertyIndex(); - instr.storeRealPair.valueIndex = index; + bool ok; + QPointF point = + QmlStringConverters::pointFFromString(string, &ok); + float data[] = { point.x(), point.y() }; + int index = output->indexForFloat(data, 2); + if (type == QVariant::PointF) + instr.type = QmlInstruction::StorePointF; + else + instr.type = QmlInstruction::StorePoint; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); + instr.storeRealPair.valueIndex = index; } break; case QVariant::Size: case QVariant::SizeF: { - bool ok; - QSizeF size = QmlStringConverters::sizeFFromString(string, &ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to size"); - float data[] = { size.width(), size.height() }; - int index = output->indexForFloat(data, 2); - if (type == QVariant::SizeF) - instr.type = QmlInstruction::StoreSizeF; - else - instr.type = QmlInstruction::StoreSize; - instr.storeRealPair.propertyIndex = prop.propertyIndex(); - instr.storeRealPair.valueIndex = index; + bool ok; + QSizeF size = QmlStringConverters::sizeFFromString(string, &ok); + float data[] = { size.width(), size.height() }; + int index = output->indexForFloat(data, 2); + if (type == QVariant::SizeF) + instr.type = QmlInstruction::StoreSizeF; + else + instr.type = QmlInstruction::StoreSize; + instr.storeRealPair.propertyIndex = prop.propertyIndex(); + instr.storeRealPair.valueIndex = index; } break; case QVariant::Rect: case QVariant::RectF: { - bool ok; - QRectF rect = QmlStringConverters::rectFFromString(string, &ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to rect"); - float data[] = { rect.x(), rect.y(), - rect.width(), rect.height() }; - int index = output->indexForFloat(data, 4); - if (type == QVariant::RectF) - instr.type = QmlInstruction::StoreRectF; - else - instr.type = QmlInstruction::StoreRect; - instr.storeRect.propertyIndex = prop.propertyIndex(); - instr.storeRect.valueIndex = index; + bool ok; + QRectF rect = QmlStringConverters::rectFFromString(string, &ok); + float data[] = { rect.x(), rect.y(), + rect.width(), rect.height() }; + int index = output->indexForFloat(data, 4); + if (type == QVariant::RectF) + instr.type = QmlInstruction::StoreRectF; + else + instr.type = QmlInstruction::StoreRect; + instr.storeRect.propertyIndex = prop.propertyIndex(); + instr.storeRect.valueIndex = index; } break; case QVariant::Bool: { - bool ok; - bool b = QmlStringConverters::boolFromString(string, &ok); - if (!ok) - COMPILE_EXCEPTION2(v, "Cannot convert value" << string << "to boolean"); - instr.type = QmlInstruction::StoreBool; - instr.storeBool.propertyIndex = prop.propertyIndex(); - instr.storeBool.value = b; + bool b = v->value.asBoolean(); + instr.type = QmlInstruction::StoreBool; + instr.storeBool.propertyIndex = prop.propertyIndex(); + instr.storeBool.value = b; } break; default: { - int t = prop.type(); - if (t == QVariant::UserType) - t = prop.userType(); - QmlMetaType::StringConverter converter = - QmlMetaType::customStringConverter(t); - if (converter) { - int index = output->customTypeData.count(); - instr.type = QmlInstruction::AssignCustomType; - instr.assignCustomType.propertyIndex = prop.propertyIndex(); - instr.assignCustomType.valueIndex = index; - - QmlCompiledData::CustomTypeData data; - data.index = output->indexForString(string); - data.type = t; - output->customTypeData << data; - break; - } + int t = prop.type(); + if (t == QVariant::UserType) + t = prop.userType(); + int index = output->customTypeData.count(); + instr.type = QmlInstruction::AssignCustomType; + instr.assignCustomType.propertyIndex = prop.propertyIndex(); + instr.assignCustomType.valueIndex = index; + + QmlCompiledData::CustomTypeData data; + data.index = output->indexForString(string); + data.type = t; + output->customTypeData << data; } - COMPILE_EXCEPTION2(v, "Cannot assign to property" << prop.name() << "of unknown type" << prop.type()); break; } - return true; + output->bytecode << instr; } void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) @@ -530,7 +532,7 @@ bool QmlCompiler::compile(QmlEngine *engine, compileTree(root); if (!isError()) { - out->dumpPre(); + out->dumpInstructions(); } else { reset(out, true); } @@ -544,24 +546,23 @@ void QmlCompiler::compileTree(Object *tree) { compileState.root = tree; + if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) + return; + QmlInstruction init; init.type = QmlInstruction::Init; init.line = 0; - init.init.dataSize = 0; - init.init.bindingsSize = 0; - init.init.parserStatusSize = 0; + init.init.bindingsSize = compileState.bindings.count(); + init.init.parserStatusSize = compileState.parserStatusCount; output->bytecode << init; - if (!compileObject(tree, 0)) // Compile failed - return; + genObject(tree); QmlInstruction def; init.line = 0; def.type = QmlInstruction::SetDefault; output->bytecode << def; - finalizeComponent(0); - if (tree->metatype) static_cast<QMetaObject &>(output->root) = *tree->metaObject(); else @@ -569,55 +570,35 @@ void QmlCompiler::compileTree(Object *tree) } -bool QmlCompiler::compileObject(Object *obj, const BindingContext &ctxt) +bool QmlCompiler::buildObject(Object *obj, const BindingContext &ctxt) { Q_ASSERT (obj->type != -1); - const QmlCompiledData::TypeReference &tr = output->types.at(obj->type); + const QmlCompiledData::TypeReference &tr = + output->types.at(obj->type); obj->metatype = tr.metaObject(); + if (tr.component) obj->url = tr.component->url(); - if (output->types.at(obj->type).className == "Component") { - COMPILE_CHECK(compileComponent(obj, ctxt)); + // This object is a "Component" element + if (obj->metatype == &QmlComponent::staticMetaObject) { + COMPILE_CHECK(buildComponent(obj, ctxt)); return true; } + // Object instantiations reset the binding context BindingContext objCtxt(obj); - int createInstrIdx = output->bytecode.count(); - // Create the object - QmlInstruction create; - create.type = QmlInstruction::CreateObject; - create.line = obj->location.start.line; - create.create.data = -1; - create.create.type = obj->type; - output->bytecode << create; - - // Create the synthesized meta object - COMPILE_CHECK(compileDynamicMeta(obj)); + // Create the synthesized meta object, ignoring aliases + COMPILE_CHECK(mergeDynamicMetaProperties(obj)); + COMPILE_CHECK(buildDynamicMeta(obj, IgnoreAliases)); // 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(); - } + QmlType *type = toQmlType(obj); 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; - begin.begin.castValue = parserStatusCast; - begin.line = obj->location.start.line; - output->bytecode << begin; - + obj->parserStatusCast = type->parserStatusCast(); + if (obj->parserStatusCast != -1) compileState.parserStatusCount++; - } // Check if this is a custom parser type. Custom parser types allow // assignments to non-existant properties. These assignments are then @@ -626,57 +607,72 @@ bool QmlCompiler::compileObject(Object *obj, const BindingContext &ctxt) output->types.at(obj->type).type->customParser() != 0; QList<QmlCustomParserProperty> customProps; - QStringList deferred = deferredProperties(obj); - QList<Property *> deferredProps; + // Fetch the list of deferred properties + QStringList deferredList = deferredProperties(obj); + + // Must do id property first. This is to ensure that the id given to any + // id reference created matches the order in which the objects are + // instantiated + foreach(Property *prop, obj->properties) { + if (prop->name == "id") { + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + break; + } + } - // Compile all explicit properties specified + // Build all explicit properties specified foreach(Property *prop, obj->properties) { + if (prop->name == "id") + continue; + + bool canDefer = false; if (isCustomParser) { - // Custom parser types don't support signal properties if (testProperty(prop, obj)) { - if (deferred.contains(QString::fromLatin1(prop->name.constData()))) - deferredProps << prop; - else - COMPILE_CHECK(compileProperty(prop, obj, objCtxt)); + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); } else { customProps << QmlCustomParserNodePrivate::fromProperty(prop); } } else { - if (isSignalPropertyName(prop->name)) { - COMPILE_CHECK(compileSignal(prop,obj)); + if (isSignalPropertyName(prop->name)) { + COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); } else { - if (deferred.contains(QString::fromLatin1(prop->name.constData()))) - deferredProps << prop; - else - COMPILE_CHECK(compileProperty(prop, obj, objCtxt)); + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); } } + if (canDefer && !deferredList.isEmpty() && + deferredList.contains(prop->name)) + prop->isDeferred = true; + } - // Compile the default property + // Build the default property if (obj->defaultProperty) { Property *prop = obj->defaultProperty; + bool canDefer = false; if (isCustomParser) { if (testProperty(prop, obj)) { - QMetaProperty p = deferred.isEmpty()?QMetaProperty():QmlMetaType::defaultProperty(obj->metaObject()); - if (deferred.contains(QString::fromLatin1(p.name()))) - deferredProps << prop; - else - COMPILE_CHECK(compileProperty(prop, obj, objCtxt)); + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); } else { customProps << QmlCustomParserNodePrivate::fromProperty(prop); } } else { - QMetaProperty p = deferred.isEmpty()?QMetaProperty():QmlMetaType::defaultProperty(obj->metaObject()); - if (deferred.contains(QString::fromLatin1(p.name()))) - deferredProps << prop; - else - COMPILE_CHECK(compileProperty(prop, obj, objCtxt)); + int ids = compileState.ids.count(); + COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); + canDefer = ids == compileState.ids.count(); } + if (canDefer && !deferredList.isEmpty() && + deferredList.contains(prop->name)) + prop->isDeferred = true; } // Compile custom parser parts @@ -684,16 +680,83 @@ bool QmlCompiler::compileObject(Object *obj, const BindingContext &ctxt) // ### Check for failure bool ok = false; QmlCustomParser *cp = output->types.at(obj->type).type->customParser(); - QByteArray customData = cp->compile(customProps, &ok); + obj->custom = cp->compile(customProps, &ok); if(!ok) COMPILE_EXCEPTION("Failure compiling custom type"); - if(!customData.isEmpty()) - output->bytecode[createInstrIdx].create.data = - output->indexForByteArray(customData); } - // Build the deferred block - if (!deferredProps.isEmpty()) { + return true; +} + +void QmlCompiler::genObject(QmlParser::Object *obj) +{ + if (obj->metatype == &QmlComponent::staticMetaObject) { + genComponent(obj); + return; + } + + // Create the object + QmlInstruction create; + create.type = QmlInstruction::CreateObject; + create.line = obj->location.start.line; + create.create.data = -1; + if (!obj->custom.isEmpty()) + create.create.data = output->indexForByteArray(obj->custom); + create.create.type = obj->type; + output->bytecode << create; + + // Setup the synthesized meta object if necessary + if (!obj->metadata.isEmpty()) { + QmlInstruction meta; + meta.type = QmlInstruction::StoreMetaObject; + meta.line = -1; + meta.storeMeta.data = output->indexForByteArray(obj->metadata); + meta.storeMeta.aliasData = output->indexForByteArray(obj->synthdata); + meta.storeMeta.slotData = -1; + output->bytecode << meta; + } + + // Set the object id + if (!obj->id.isEmpty()) { + QmlInstruction id; + id.type = QmlInstruction::SetId; + id.line = -1; + id.setId.value = output->indexForString(obj->id); + output->bytecode << id; + } + + // Begin the class + if (obj->parserStatusCast != -1) { + QmlInstruction begin; + begin.type = QmlInstruction::BeginObject; + begin.begin.castValue = obj->parserStatusCast; + begin.line = obj->location.start.line; + output->bytecode << begin; + } + + genObjectBody(obj); + + // Complete the the class + if (obj->parserStatusCast != -1) { + QmlInstruction complete; + complete.type = QmlInstruction::CompleteObject; + complete.complete.castValue = obj->parserStatusCast; + complete.line = obj->location.start.line; + output->bytecode << complete; + } +} + +void QmlCompiler::genObjectBody(QmlParser::Object *obj) +{ + bool seenDefer = false; + foreach(Property *prop, obj->valueProperties) { + if (prop->isDeferred) { + seenDefer = true; + continue; + } + genValueProperty(prop, obj); + } + if (seenDefer) { QmlInstruction defer; defer.type = QmlInstruction::Defer; defer.line = 0; @@ -701,44 +764,147 @@ bool QmlCompiler::compileObject(Object *obj, const BindingContext &ctxt) int deferIdx = output->bytecode.count(); output->bytecode << defer; - // ### This is lame, we should check if individual properties have - // ids defined within them - int idCount = compileState.ids.count(); - foreach (Property *prop, deferredProps) { - COMPILE_CHECK(compileProperty(prop, obj, objCtxt)); + foreach(Property *prop, obj->valueProperties) { + if (!prop->isDeferred) + continue; + genValueProperty(prop, obj); } - if (idCount == compileState.ids.count()) - output->bytecode[deferIdx].defer.deferCount = - output->bytecode.count() - deferIdx - 1; + + output->bytecode[deferIdx].defer.deferCount = + output->bytecode.count() - deferIdx - 1; } - // If the type support the QmlParserStatusInterface we need to invoke - // classComplete() - if (parserStatusCast != -1) { - QmlInstruction complete; - complete.type = QmlInstruction::CompleteObject; - complete.complete.castValue = parserStatusCast; - complete.line = obj->location.start.line; - output->bytecode << complete; - } + foreach(Property *prop, obj->signalProperties) { - return true; + QmlParser::Value *v = prop->values.at(0); + + if (v->type == Value::SignalObject) { + + genObject(v->object); + + QmlInstruction assign; + assign.type = QmlInstruction::AssignSignalObject; + assign.line = v->location.start.line; + assign.assignSignalObject.signal = + output->indexForByteArray(prop->name); + output->bytecode << assign; + + } else if (v->type == Value::SignalExpression) { + + QmlInstruction store; + store.type = QmlInstruction::StoreSignal; + store.line = v->location.start.line; + store.storeSignal.signalIndex = prop->index; + store.storeSignal.value = + output->indexForString(v->value.asScript().trimmed()); + output->bytecode << store; + + } + + } + + foreach(Property *prop, obj->attachedProperties) { + QmlInstruction fetch; + fetch.type = QmlInstruction::FetchAttached; + fetch.line = prop->location.start.line; + fetch.fetchAttached.id = prop->index; + output->bytecode << fetch; + + genObjectBody(prop->value); + + QmlInstruction pop; + pop.type = QmlInstruction::PopFetchedObject; + pop.line = prop->location.start.line; + output->bytecode << pop; + } + + foreach(Property *prop, obj->groupedProperties) { + QmlInstruction fetch; + fetch.type = QmlInstruction::FetchObject; + fetch.fetch.property = prop->index; + fetch.line = prop->location.start.line; + output->bytecode << fetch; + + genObjectBody(prop->value); + + QmlInstruction pop; + pop.type = QmlInstruction::PopFetchedObject; + pop.line = prop->location.start.line; + output->bytecode << pop; + } +} + +void QmlCompiler::genComponent(QmlParser::Object *obj) +{ + QmlParser::Object *root = obj->defaultProperty->values.at(0)->object; + Q_ASSERT(root); + + QmlInstruction create; + create.type = QmlInstruction::CreateComponent; + create.line = root->location.start.line; + create.createComponent.endLine = root->location.end.line; + output->bytecode << create; + int count = output->bytecode.count(); + + ComponentCompileState oldCompileState = compileState; + compileState = componentState(root); + + QmlInstruction init; + init.type = QmlInstruction::Init; + init.init.bindingsSize = compileState.bindings.count(); + init.init.parserStatusSize = compileState.parserStatusCount; + init.line = obj->location.start.line; + output->bytecode << init; + + genObject(root); + + output->bytecode[count - 1].createComponent.count = + output->bytecode.count() - count; + + compileState = oldCompileState; + + if (!obj->id.isEmpty()) { + QmlInstruction id; + id.type = QmlInstruction::SetId; + id.line = -1; + id.setId.value = output->indexForString(obj->id);; + output->bytecode << id; + } } -bool QmlCompiler::compileComponent(Object *obj, const BindingContext &ctxt) +bool QmlCompiler::buildComponent(QmlParser::Object *obj, + const BindingContext &ctxt) { + // The special "Component" element can only have the id property and a + // default property, that actually defines the component's tree + + // Find, check and set the "id" property (if any) Property *idProp = 0; if (obj->properties.count() > 1 || (obj->properties.count() == 1 && obj->properties.begin().key() != "id")) COMPILE_EXCEPTION("Invalid component specification"); + + if (obj->properties.count()) + idProp = *obj->properties.begin(); + if (idProp && (idProp->value || idProp->values.count() > 1 || !isValidId(idProp->values.first()->primitive()))) + COMPILE_EXCEPTION("Invalid component id specification"); + + if (idProp) { + QString idVal = idProp->values.first()->primitive().toUtf8(); + + if (compileState.ids.contains(idVal)) + COMPILE_EXCEPTION("id is not unique"); + + addId(idVal, obj); + + obj->id = idVal.toUtf8(); + } + + // Check the Component tree is well formed if (obj->defaultProperty && (obj->defaultProperty->value || obj->defaultProperty->values.count() > 1 || (obj->defaultProperty->values.count() == 1 && !obj->defaultProperty->values.first()->object))) COMPILE_EXCEPTION("Invalid component body specification"); - if (obj->properties.count()) - idProp = *obj->properties.begin(); - if (idProp && (idProp->value || idProp->values.count() > 1)) - COMPILE_EXCEPTION("Invalid component id specification"); Object *root = 0; if (obj->defaultProperty && obj->defaultProperty->values.count()) @@ -747,84 +913,68 @@ bool QmlCompiler::compileComponent(Object *obj, const BindingContext &ctxt) if (!root) COMPILE_EXCEPTION("Cannot create empty component specification"); - COMPILE_CHECK(compileComponentFromRoot(root, ctxt)); - - if (idProp && idProp->values.count()) { - QString val = idProp->values.at(0)->primitive(); - if (!isValidId(val)) - COMPILE_EXCEPTION("Invalid id property value"); - - if (compileState.ids.contains(val)) - COMPILE_EXCEPTION("id is not unique"); - - IdReference reference; - reference.id = val; - reference.object = obj; - reference.instructionIdx = output->bytecode.count(); - reference.idx = compileState.ids.count(); - compileState.ids.insert(val, reference); - - int pref = output->indexForString(val); - QmlInstruction id; - id.type = QmlInstruction::SetId; - id.line = idProp->location.start.line; - id.setId.value = pref; - id.setId.save = -1; - - output->bytecode << id; - } + // Build the component tree + COMPILE_CHECK(buildComponentFromRoot(root, ctxt)); return true; } -bool QmlCompiler::compileComponentFromRoot(Object *obj, const BindingContext &ctxt) +bool QmlCompiler::buildComponentFromRoot(QmlParser::Object *obj, + const BindingContext &ctxt) { - output->bytecode.push_back(QmlInstruction()); - QmlInstruction &create = output->bytecode.last(); - create.type = QmlInstruction::CreateComponent; - create.line = obj->location.start.line; - create.createComponent.endLine = obj->location.end.line; - int count = output->bytecode.count(); - - QmlInstruction init; - init.type = QmlInstruction::Init; - init.init.dataSize = 0; - init.init.bindingsSize = 0; - init.init.parserStatusSize = 0; - init.line = obj->location.start.line; - output->bytecode << init; - ComponentCompileState oldComponentCompileState = compileState; compileState = ComponentCompileState(); compileState.root = obj; + if (obj) - COMPILE_CHECK(compileObject(obj, ctxt)); + COMPILE_CHECK(buildObject(obj, ctxt)); + + COMPILE_CHECK(completeComponentBuild()); - COMPILE_CHECK(finalizeComponent(count)); - create.createComponent.count = output->bytecode.count() - count; compileState = oldComponentCompileState; + return true; } -bool QmlCompiler::compileFetchedObject(Object *obj, const BindingContext &ctxt) + +// Build a sub-object. A sub-object is one that was not created directly by +// QML - such as a grouped property object, or an attached object. Sub-object's +// can't have an id, involve a custom parser, have attached properties etc. +bool QmlCompiler::buildSubObject(Object *obj, const BindingContext &ctxt) { Q_ASSERT(obj->metatype); + Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding + // sub-context if (obj->defaultProperty) - COMPILE_CHECK(compileProperty(obj->defaultProperty, obj, ctxt)); + COMPILE_CHECK(buildProperty(obj->defaultProperty, obj, ctxt)); foreach(Property *prop, obj->properties) { if (isSignalPropertyName(prop->name)) { - COMPILE_CHECK(compileSignal(prop, obj)); + COMPILE_CHECK(buildSignal(prop, obj, ctxt)); } else { - COMPILE_CHECK(compileProperty(prop, obj, ctxt)); + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); } } return true; } -int QmlCompiler::signalByName(const QMetaObject *mo, const QByteArray &name) +int QmlCompiler::componentTypeRef() +{ + QmlType *t = QmlMetaType::qmlType("Component"); + for (int ii = output->types.count() - 1; ii >= 0; --ii) { + if (output->types.at(ii).type == t) + return ii; + } + QmlCompiledData::TypeReference ref; + ref.className = "Component"; + ref.type = t; + output->types << ref; + return output->types.count() - 1; +} + +int QmlCompiler::findSignalByName(const QMetaObject *mo, const QByteArray &name) { int methods = mo->methodCount(); for (int ii = methods - 1; ii >= 0; --ii) { @@ -839,15 +989,13 @@ int QmlCompiler::signalByName(const QMetaObject *mo, const QByteArray &name) return -1; } -bool QmlCompiler::compileSignal(Property *prop, Object *obj) +bool QmlCompiler::buildSignal(QmlParser::Property *prop, QmlParser::Object *obj, + const BindingContext &ctxt) { Q_ASSERT(obj->metaObject()); - if (prop->values.isEmpty() && !prop->value) - return true; - - if (prop->value || prop->values.count() > 1) - COMPILE_EXCEPTION("Incorrectly specified signal"); + if (prop->isEmpty()) + COMPILE_EXCEPTION2(prop, "Empty property assignment"); QByteArray name = prop->name; Q_ASSERT(name.startsWith("on")); @@ -855,47 +1003,26 @@ bool QmlCompiler::compileSignal(Property *prop, Object *obj) if(name[0] >= 'A' && name[0] <= 'Z') name[0] = name[0] - 'A' + 'a'; - int sigIdx = signalByName(obj->metaObject(), name); + int sigIdx = findSignalByName(obj->metaObject(), name); if (sigIdx == -1) { - COMPILE_CHECK(compileProperty(prop, obj, 0)); + // If the "on<Signal>" name doesn't resolve into a signal, try it as a + // property. + COMPILE_CHECK(buildProperty(prop, obj, ctxt)); } else { - if (prop->values.at(0)->object) { - int pr = output->indexForByteArray(prop->name); - - bool rv = compileObject(prop->values.at(0)->object, 0); + if (prop->value || prop->values.count() > 1) + COMPILE_EXCEPTION("Incorrectly specified signal"); - if (rv) { - QmlInstruction assign; - assign.type = QmlInstruction::AssignSignalObject; - assign.line = prop->values.at(0)->location.start.line; - assign.assignSignalObject.signal = pr; - - output->bytecode << assign; - - prop->values.at(0)->type = Value::SignalObject; - } - - return rv; + prop->index = sigIdx; + obj->addSignalProperty(prop); + if (prop->values.at(0)->object) { + COMPILE_CHECK(buildObject(prop->values.at(0)->object, ctxt)); + prop->values.at(0)->type = Value::SignalObject; } else { - QString script = prop->values.at(0)->value.asScript().trimmed(); - if (script.isEmpty()) - return true; - - int idx = output->indexForString(script); - - QmlInstruction store; - store.line = prop->values.at(0)->location.start.line; - store.type = QmlInstruction::StoreSignal; - store.storeSignal.signalIndex = sigIdx; - store.storeSignal.value = idx; - - output->bytecode << store; - prop->values.at(0)->type = Value::SignalExpression; } } @@ -903,6 +1030,7 @@ bool QmlCompiler::compileSignal(Property *prop, Object *obj) return true; } + // Returns true if prop exists on obj, false otherwise bool QmlCompiler::testProperty(QmlParser::Property *prop, QmlParser::Object *obj) @@ -924,9 +1052,11 @@ bool QmlCompiler::testProperty(QmlParser::Property *prop, return false; } -bool QmlCompiler::compileProperty(Property *prop, Object *obj, const BindingContext &ctxt) +bool QmlCompiler::buildProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) { - if (prop->values.isEmpty() && !prop->value) + if (prop->isEmpty()) COMPILE_EXCEPTION2(prop, "Empty property assignment"); const QMetaObject *metaObject = obj->metaObject(); @@ -934,13 +1064,21 @@ bool QmlCompiler::compileProperty(Property *prop, Object *obj, const BindingCont if (isAttachedPropertyName(prop->name)) { // Setup attached property data + + if (ctxt.isSubContext()) { + // Attached properties cannot be used on sub-objects. Sub-objects + // always exist in a binding sub-context, which is what we test + // for here. + COMPILE_EXCEPTION2(prop, "Attached properties cannot be used here"); + } + QmlType *type = QmlMetaType::qmlType(prop->name); if (!type || !type->attachedPropertiesType()) COMPILE_EXCEPTION2(prop, "Non-existant attached object"); if (!prop->value) - COMPILE_EXCEPTION2(prop, "Cannot assign directly to attached object"); + COMPILE_EXCEPTION2(prop, "Invalid attached object assignment"); prop->value->metatype = type->attachedPropertiesType(); } else { @@ -976,125 +1114,259 @@ bool QmlCompiler::compileProperty(Property *prop, Object *obj, const BindingCont } } - if (!prop->isDefault && prop->name == "id") { + if (!prop->isDefault && prop->name == "id" && !ctxt.isSubContext()) { - COMPILE_CHECK(compileIdProperty(prop, obj)); + // The magic "id" behaviour doesn't apply when "id" is resolved as a + // default property or to sub-objects (which are always in binding + // sub-contexts) + COMPILE_CHECK(buildIdProperty(prop, obj)); + if (prop->type == QVariant::String && + prop->values.at(0)->value.isString()) + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); } else if (isAttachedPropertyName(prop->name)) { - COMPILE_CHECK(compileAttachedProperty(prop, ctxt)); + COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); } else if (prop->index == -1) { if (prop->isDefault) { - COMPILE_EXCEPTION2(prop, "Cannot assign to non-existant default property"); + COMPILE_EXCEPTION2(prop->values.first(), "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)); + COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); } else if (QmlMetaType::isQmlList(prop->type) || QmlMetaType::isList(prop->type)) { - COMPILE_CHECK(compileListProperty(prop, obj, ctxt)); + COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); } else { - COMPILE_CHECK(compilePropertyAssignment(prop, obj, ctxt)); + COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); } return true; } -bool QmlCompiler::compileIdProperty(QmlParser::Property *prop, - QmlParser::Object *obj) +void QmlCompiler::genValueProperty(QmlParser::Property *prop, + QmlParser::Object *obj) +{ + if (QmlMetaType::isQmlList(prop->type) || QmlMetaType::isList(prop->type)) { + genListProperty(prop, obj); + } else { + genPropertyAssignment(prop, obj); + } +} + +void QmlCompiler::genListProperty(QmlParser::Property *prop, + QmlParser::Object *obj) +{ + QmlInstruction::Type fetchType; + QmlInstruction::Type storeType; + int listType; + + if (QmlMetaType::isQmlList(prop->type)) { + fetchType = QmlInstruction::FetchQmlList; + storeType = QmlInstruction::StoreObjectQmlList; + listType = QmlMetaType::qmlListType(prop->type); + } else { + fetchType = QmlInstruction::FetchQList; + storeType = QmlInstruction::StoreObjectQList; + listType = QmlMetaType::listType(prop->type); + } + + QmlInstruction fetch; + fetch.type = fetchType; + fetch.line = prop->location.start.line; + fetch.fetchQmlList.property = prop->index; + bool listTypeIsInterface = QmlMetaType::isInterface(listType); + fetch.fetchQmlList.type = listType; + output->bytecode << fetch; + + for (int ii = 0; ii < prop->values.count(); ++ii) { + Value *v = prop->values.at(ii); + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + if (listTypeIsInterface) { + QmlInstruction assign; + assign.type = QmlInstruction::AssignObjectList; + assign.line = prop->location.start.line; + output->bytecode << assign; + } else { + QmlInstruction store; + store.type = storeType; + store.line = prop->location.start.line; + output->bytecode << store; + } + + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj); + + } + + } + + QmlInstruction pop; + pop.type = QmlInstruction::PopQList; + pop.line = prop->location.start.line; + output->bytecode << pop; +} + +void QmlCompiler::genPropertyAssignment(QmlParser::Property *prop, + QmlParser::Object *obj) +{ + for (int ii = 0; ii < prop->values.count(); ++ii) { + QmlParser::Value *v = prop->values.at(ii); + + if (v->type == Value::CreatedObject) { + + genObject(v->object); + + if (QmlMetaType::isInterface(prop->type)) { + + QmlInstruction store; + store.type = QmlInstruction::StoreInterface; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } else if (prop->type == -1) { + + QmlInstruction store; + store.type = QmlInstruction::StoreVariantObject; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } else { + + QmlInstruction store; + store.type = QmlInstruction::StoreObject; + store.line = v->object->location.start.line; + store.storeObject.propertyIndex = prop->index; + output->bytecode << store; + + } + } else if (v->type == Value::ValueSource) { + + genObject(v->object); + + QmlInstruction store; + store.type = QmlInstruction::StoreValueSource; + store.line = v->object->location.start.line; + store.assignValueSource.property = prop->index; + output->bytecode << store; + + } else if (v->type == Value::PropertyBinding) { + + genBindingAssignment(v, prop, obj); + + } else if (v->type == Value::Literal) { + + QMetaProperty mp = obj->metaObject()->property(prop->index); + genLiteralAssignment(mp, v); + + } + + } +} + +bool QmlCompiler::buildIdProperty(QmlParser::Property *prop, + QmlParser::Object *obj) { - if (prop->value) - COMPILE_EXCEPTION2(prop,"The id property cannot be fetched"); - if (prop->values.count() > 1) - COMPILE_EXCEPTION2(prop, "The object id may only be set once"); + if (prop->value || + prop->values.count() > 1 || + prop->values.at(0)->object) + COMPILE_EXCEPTION2(prop, "Invalid use of id property"); - if (prop->values.at(0)->object) - COMPILE_EXCEPTION("Cannot assign an object as an id"); QString val = prop->values.at(0)->primitive(); + if (!isValidId(val)) - COMPILE_EXCEPTION(val << "is not a valid id"); + COMPILE_EXCEPTION2(prop, val << "is not a valid object id"); + if (compileState.ids.contains(val)) - COMPILE_EXCEPTION("id is not unique"); + COMPILE_EXCEPTION2(prop, "id is not unique"); - int pref = output->indexForString(val); + obj->id = val.toUtf8(); - if (prop->type == QVariant::String) { - QmlInstruction assign; - assign.type = QmlInstruction::StoreString; - assign.storeString.propertyIndex = prop->index; - assign.storeString.value = pref; - assign.line = prop->values.at(0)->location.start.line; - output->bytecode << assign; + prop->values.at(0)->type = Value::Id; - prop->values.at(0)->type = Value::Id; - } else { - prop->values.at(0)->type = Value::Literal; - } + addId(val, obj); + return true; +} + +void QmlCompiler::addId(const QString &id, QmlParser::Object *obj) +{ IdReference reference; - reference.id = val; + reference.id = id; reference.object = obj; - reference.instructionIdx = output->bytecode.count(); reference.idx = compileState.ids.count(); - compileState.ids.insert(val, reference); + compileState.ids.insert(id, reference); +} - QmlInstruction id; - id.type = QmlInstruction::SetId; - id.line = prop->values.at(0)->location.start.line; - id.setId.value = pref; - id.setId.save = -1; - output->bytecode << id; +void QmlCompiler::addBindingReference(const BindingReference &ref) +{ + int id = compileState.bindings.count(); + compileState.bindings << ref; + compileState.bindingMap.insert(ref.value, id); +} - obj->id = val.toLatin1(); +void QmlCompiler::saveComponentState() +{ + Q_ASSERT(compileState.root); + Q_ASSERT(!savedCompileStates.contains(compileState.root)); - return true; + savedCompileStates.insert(compileState.root, compileState); } -// Compile attached property object. In this example, +QmlCompiler::ComponentCompileState +QmlCompiler::componentState(QmlParser::Object *obj) +{ + Q_ASSERT(savedCompileStates.contains(obj)); + return savedCompileStates.value(obj); +} + +// Build attached property object. In this example, // Text { // GridView.row: 10 // } // GridView is an attached property object. -bool QmlCompiler::compileAttachedProperty(QmlParser::Property *prop, - const BindingContext &ctxt) +bool QmlCompiler::buildAttachedProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) { 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; - fetch.fetchAttached.id = id; - output->bytecode << fetch; - - COMPILE_CHECK(compileFetchedObject(prop->value, ctxt.incr())); + prop->index = id; + obj->addAttachedProperty(prop); - QmlInstruction pop; - pop.type = QmlInstruction::PopFetchedObject; - pop.line = prop->location.start.line; - output->bytecode << pop; + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); return true; } -// Compile "nested" properties. In this example: + +// Build "grouped" properties. In this example: // Text { // font.size: 12 +// font.family: "Helvetica" // } -// font is a nested property. size is not. -bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, - const BindingContext &ctxt) +// font is a nested property. size and family are not. +bool QmlCompiler::buildGroupedProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) { Q_ASSERT(prop->type != 0); Q_ASSERT(prop->index != -1); @@ -1104,132 +1376,90 @@ bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, if (!prop->value->metatype) COMPILE_EXCEPTION2(prop, "Cannot nest non-QObject property" << prop->name); - QmlInstruction fetch; - fetch.type = QmlInstruction::FetchObject; - fetch.fetch.property = prop->index; - fetch.fetch.isObject = true; - fetch.line = prop->location.start.line; - output->bytecode << fetch; + obj->addGroupedProperty(prop); - COMPILE_CHECK(compileFetchedObject(prop->value, ctxt.incr())); - - QmlInstruction pop; - pop.type = QmlInstruction::PopFetchedObject; - pop.line = prop->location.start.line; - output->bytecode << pop; + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); return true; } -// Compile assignments to QML lists. QML lists are properties of type + +// Build 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, - const BindingContext &ctxt) +bool QmlCompiler::buildListProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) { Q_ASSERT(QmlMetaType::isList(prop->type) || QmlMetaType::isQmlList(prop->type)); int t = prop->type; + obj->addValueProperty(prop); + if (QmlMetaType::isQmlList(t)) { - QmlInstruction fetch; - fetch.line = prop->location.start.line; - fetch.type = QmlInstruction::FetchQmlList; - fetch.fetchQmlList.property = prop->index; int listType = QmlMetaType::qmlListType(t); bool listTypeIsInterface = QmlMetaType::isInterface(listType); - fetch.fetchQmlList.type = listType; - output->bytecode << fetch; for (int ii = 0; ii < prop->values.count(); ++ii) { Value *v = prop->values.at(ii); if (v->object) { v->type = Value::CreatedObject; - COMPILE_CHECK(compileObject(v->object, ctxt)); + COMPILE_CHECK(buildObject(v->object, ctxt)); + // We check object coercian here. We check interface assignment + // at runtime. if (!listTypeIsInterface) { - if (canConvert(listType, v->object)) { - QmlInstruction store; - store.type = QmlInstruction::StoreObjectQmlList; - store.line = prop->location.start.line; - output->bytecode << store; - } else { + if (!canCoerce(listType, v->object)) { COMPILE_EXCEPTION("Cannot assign object to list"); } - - } else { - QmlInstruction assign; - assign.type = QmlInstruction::AssignObjectList; - assign.line = prop->location.start.line; - output->bytecode << assign; } + } else { COMPILE_EXCEPTION("Cannot assign primitives to lists"); } } - QmlInstruction pop; - pop.type = QmlInstruction::PopQList; - pop.line = prop->location.start.line; - output->bytecode << pop; } else { - QmlInstruction fetch; - fetch.type = QmlInstruction::FetchQList; - fetch.line = prop->location.start.line; - fetch.fetchQmlList.property = prop->index; int listType = QmlMetaType::listType(t); bool listTypeIsInterface = QmlMetaType::isInterface(listType); - fetch.fetchQmlList.type = listType; - output->bytecode << fetch; bool assignedBinding = false; for (int ii = 0; ii < prop->values.count(); ++ii) { Value *v = prop->values.at(ii); if (v->object) { v->type = Value::CreatedObject; - COMPILE_CHECK(compileObject(v->object, ctxt)); + COMPILE_CHECK(buildObject(v->object, ctxt)); + // We check object coercian here. We check interface assignment + // at runtime. if (!listTypeIsInterface) { - if (canConvert(listType, v->object)) { - QmlInstruction store; - store.type = QmlInstruction::StoreObjectQList; - store.line = prop->location.start.line; - output->bytecode << store; - } else { + if (!canCoerce(listType, v->object)) { COMPILE_EXCEPTION("Cannot assign object to list"); } - } else { - QmlInstruction assign; - assign.type = QmlInstruction::AssignObjectList; - assign.line = v->location.start.line; - output->bytecode << assign; - } + } + } else if (v->value.isScript()) { if (assignedBinding) COMPILE_EXCEPTION("Can only assign one binding to lists"); assignedBinding = true; - COMPILE_CHECK(compileBinding(v, prop, ctxt)); + COMPILE_CHECK(buildBinding(v, prop, ctxt)); v->type = Value::PropertyBinding; } else { COMPILE_EXCEPTION("Cannot assign primitives to lists"); } } - QmlInstruction pop; - pop.line = prop->location.start.line; - pop.type = QmlInstruction::PopQList; - output->bytecode << pop; } return true; } -// Compile regular property assignments of the form property: <value> +// Compile regular property assignments of the form "property: <value>" // // ### The following problems exist // @@ -1244,19 +1474,21 @@ bool QmlCompiler::compileListProperty(QmlParser::Property *prop, // } // // We allow assignming multiple values to single value properties -bool QmlCompiler::compilePropertyAssignment(QmlParser::Property *prop, - QmlParser::Object *obj, - const BindingContext &ctxt) +bool QmlCompiler::buildPropertyAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + const BindingContext &ctxt) { + obj->addValueProperty(prop); + for (int ii = 0; ii < prop->values.count(); ++ii) { Value *v = prop->values.at(ii); if (v->object) { - COMPILE_CHECK(compilePropertyObjectAssignment(prop, v, ctxt)); + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); } else { - COMPILE_CHECK(compilePropertyLiteralAssignment(prop, obj, v, ctxt)); + COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); } } @@ -1265,9 +1497,10 @@ bool QmlCompiler::compilePropertyAssignment(QmlParser::Property *prop, } // Compile assigning a single object instance to a regular property -bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, - QmlParser::Value *v, - const BindingContext &ctxt) +bool QmlCompiler::buildPropertyObjectAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *v, + const BindingContext &ctxt) { Q_ASSERT(prop->index != -1); Q_ASSERT(v->object->type != -1); @@ -1275,30 +1508,18 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, 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; - output->bytecode << assign; + COMPILE_CHECK(buildObject(v->object, ctxt)); v->type = Value::CreatedObject; } else if (prop->type == -1) { // 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; - output->bytecode << assign; + COMPILE_CHECK(buildObject(v->object, ctxt)); v->type = Value::CreatedObject; } else { - // Normally compileObject() will set this up, but we need the static + // Normally buildObject() 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 @@ -1333,39 +1554,29 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, 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; - output->bytecode << assign; + COMPILE_CHECK(buildObject(v->object, ctxt)); v->type = Value::CreatedObject; } 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; - output->bytecode << assign; - - v->type = Value::Component; + QmlParser::Object *root = v->object; + QmlParser::Object *component = new QmlParser::Object; + component->type = componentTypeRef(); + component->typeName = "Component"; + component->metatype = &QmlComponent::staticMetaObject; + component->location = root->location; + QmlParser::Value *componentValue = new QmlParser::Value; + componentValue->object = root; + component->getDefaultProperty()->addValue(componentValue); + v->object = component; + COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); } else if (isPropertyValue) { // Assign as a property value source - COMPILE_CHECK(compileObject(v->object, ctxt)); - - QmlInstruction assign; - assign.type = QmlInstruction::StoreValueSource; - assign.line = v->object->location.start.line; - assign.assignValueSource.property = prop->index; - output->bytecode << assign; + COMPILE_CHECK(buildObject(v->object, ctxt)); v->type = Value::ValueSource; } else { - COMPILE_EXCEPTION2(v->object, "Unassignable object"); + COMPILE_EXCEPTION2(v->object, "Cannot assign object to property"); } } @@ -1373,25 +1584,22 @@ bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, } // Compile assigning a literal or binding to a regular property -bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, - QmlParser::Object *obj, - QmlParser::Value *v, - const BindingContext &ctxt) +bool QmlCompiler::buildPropertyLiteralAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *v, + const BindingContext &ctxt) { Q_ASSERT(prop->index != -1); if (v->value.isScript()) { - COMPILE_CHECK(compileBinding(v, prop, ctxt)); + COMPILE_CHECK(buildBinding(v, prop, ctxt)); v->type = Value::PropertyBinding; } else { - QmlInstruction assign; - assign.line = v->location.start.line; - COMPILE_CHECK(compileStoreInstruction(assign, obj->metaObject()->property(prop->index), v)); - output->bytecode << assign; + COMPILE_CHECK(testLiteralAssignment(obj->metaObject()->property(prop->index), v)); v->type = Value::Literal; } @@ -1399,31 +1607,99 @@ bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, return true; } -bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) +// Ensures that the dynamic meta specification on obj is valid +bool QmlCompiler::checkDynamicMeta(QmlParser::Object *obj) +{ + QSet<QByteArray> propNames; + QSet<QByteArray> methodNames; + bool seenDefaultProperty = false; + + // Check properties + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const QmlParser::Object::DynamicProperty &prop = + obj->dynamicProperties.at(ii); + + if (prop.isDefaultProperty) { + if (seenDefaultProperty) + COMPILE_EXCEPTION("Duplicate default property"); + seenDefaultProperty = true; + } + + if (propNames.contains(prop.name)) + COMPILE_EXCEPTION("Duplicate property name"); + + propNames.insert(prop.name); + } + + for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { + QByteArray name = obj->dynamicSignals.at(ii).name; + if (methodNames.contains(name)) + COMPILE_EXCEPTION("Duplicate signal name"); + methodNames.insert(name); + } + for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { + QByteArray name = obj->dynamicSlots.at(ii).name; + if (methodNames.contains(name)) + COMPILE_EXCEPTION("Duplicate method name"); + methodNames.insert(name); + } + + return true; +} + +bool QmlCompiler::mergeDynamicMetaProperties(QmlParser::Object *obj) +{ + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if (!p.defaultValue || p.type == Object::DynamicProperty::Alias) + continue; + + Property *property = 0; + if (p.isDefaultProperty) + property = obj->getDefaultProperty(); + else + property = obj->getProperty(p.name); + + if (property->value) + COMPILE_EXCEPTION2(property, "Invalid property nesting"); + + for (int ii = 0; ii < p.defaultValue->values.count(); ++ii) { + Value *v = p.defaultValue->values.at(ii); + v->addref(); + property->values.append(v); + } + } + return true; +} + +bool QmlCompiler::buildDynamicMeta(QmlParser::Object *obj, DynamicMetaMode mode) { - // ### FIXME - Check that there is only one default property etc. if (obj->dynamicProperties.isEmpty() && obj->dynamicSignals.isEmpty() && obj->dynamicSlots.isEmpty()) return true; + COMPILE_CHECK(checkDynamicMeta(obj)); + QByteArray dynamicData(sizeof(QmlVMEMetaData), (char)0); QMetaObjectBuilder builder; if (obj->metatype) - builder.setClassName(QByteArray(obj->metatype->className()) + "QML"); - + builder.setClassName(QByteArray(obj->metatype->className()) + "_QML"); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + bool hasAlias = false; for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); if (p.isDefaultProperty && - (p.type != Object::DynamicProperty::Alias || preAlias != -1)) + (p.type != Object::DynamicProperty::Alias || + mode == ResolveAliases)) builder.addClassInfo("DefaultProperty", p.name); QByteArray type; - int propertyType; + int propertyType = 0; switch(p.type) { case Object::DynamicProperty::Alias: hasAlias = true; @@ -1471,7 +1747,7 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) builder.addProperty(p.name, type, ii); } - if (preAlias != -1) { + if (mode == ResolveAliases) { for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); @@ -1495,8 +1771,6 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) ((QmlVMEMetaData *)dynamicData.data())->signalCount++; } - int slotStart = obj->dynamicSlots.isEmpty()?-1:output->primitives.count(); - for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { const Object::DynamicSlot &s = obj->dynamicSlots.at(ii); QByteArray sig(s.name + "("); @@ -1509,57 +1783,37 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) b.setParameterNames(s.parameterNames); ((QmlVMEMetaData *)dynamicData.data())->methodCount++; - QmlVMEMetaData::MethodData methodData = { s.parameterNames.count() }; - dynamicData.append((char *)&methodData, sizeof(methodData)); + QmlVMEMetaData::MethodData methodData = + { s.parameterNames.count(), 0, s.body.length(), 0 }; - if (preAlias == -1) - output->primitives << s.body; + dynamicData.append((char *)&methodData, sizeof(methodData)); } - if (obj->metatype) - builder.setSuperClass(obj->metatype); - - obj->extObjectData = builder.toMetaObject(); - static_cast<QMetaObject &>(obj->extObject) = *obj->extObjectData; - - if (preAlias != -1) { - QmlInstruction &store = output->bytecode[preAlias]; - - store.storeMeta.aliasData = output->indexForByteArray(dynamicData); - qFree(output->synthesizedMetaObjects.at(store.storeMeta.data)); - output->synthesizedMetaObjects[store.storeMeta.data] = obj->extObjectData; - - } else { - output->synthesizedMetaObjects << obj->extObjectData; - QmlInstruction store; - store.type = QmlInstruction::StoreMetaObject; - store.storeMeta.data = output->synthesizedMetaObjects.count() - 1; - store.storeMeta.slotData = slotStart; - store.storeMeta.aliasData = output->indexForByteArray(dynamicData); - store.line = obj->location.start.line; - output->bytecode << store; - - if (hasAlias) { - AliasReference alias; - alias.object = obj; - alias.instructionIdx = output->bytecode.count() - 1; - compileState.aliases << alias; - } + for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { + const Object::DynamicSlot &s = obj->dynamicSlots.at(ii); + QmlVMEMetaData::MethodData *data = + ((QmlVMEMetaData *)dynamicData.data())->methodData() + ii; + + data->bodyOffset = dynamicData.size(); + + dynamicData.append((const char *)s.body.constData(), + (s.body.length() * sizeof(QChar))); } - for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { - const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + obj->metadata = builder.toRelocatableData(); + builder.fromRelocatableData(&obj->extObject, obj->metatype, obj->metadata); - if (p.type == Object::DynamicProperty::Alias) - continue; + // ### Remove me + obj->extObjectData = &obj->extObject; - if (p.defaultValue) { - p.defaultValue->name = p.name; - p.defaultValue->isDefault = false; - COMPILE_CHECK(compileProperty(p.defaultValue, obj, 0)); - } + if (mode == IgnoreAliases && hasAlias) { + AliasReference alias; + alias.object = obj; + compileState.aliases << alias; } + obj->synthdata = dynamicData; + return true; } @@ -1623,9 +1877,9 @@ bool QmlCompiler::compileAlias(QMetaObjectBuilder &builder, return true; } -bool QmlCompiler::compileBinding(QmlParser::Value *value, - QmlParser::Property *prop, - const BindingContext &ctxt) +bool QmlCompiler::buildBinding(QmlParser::Value *value, + QmlParser::Property *prop, + const BindingContext &ctxt) { Q_ASSERT(prop->index); Q_ASSERT(prop->parent); @@ -1633,7 +1887,7 @@ bool QmlCompiler::compileBinding(QmlParser::Value *value, QMetaProperty mp = prop->parent->metaObject()->property(prop->index); if (!mp.isWritable() && !QmlMetaType::isList(prop->type)) - COMPILE_EXCEPTION2(prop, "Cannot assign binding to read-only property"); + COMPILE_EXCEPTION2(prop, "Invalid property assignment: read-only property"); BindingReference reference; reference.expression = value->value; @@ -1641,185 +1895,104 @@ bool QmlCompiler::compileBinding(QmlParser::Value *value, reference.value = value; reference.instructionIdx = output->bytecode.count(); reference.bindingContext = ctxt; - compileState.bindings << reference; - - output->bytecode << QmlInstruction();; + addBindingReference(reference); return true; } -#if 0 - -#include <iostream> -#ifdef Q_CC_GNU -#include <cxxabi.h> -#endif - -//////////////////////////////////////////////////////////////////////////////// -// AST Dump -//////////////////////////////////////////////////////////////////////////////// -class Dump: protected QmlJS::AST::Visitor -{ - std::ostream &out; - int depth; - -public: - Dump(std::ostream &out) - : out(out), depth(-1) - { } - - void operator()(QmlJS::AST::Node *node) - { QmlJS::AST::Node::acceptChild(node, this); } - -protected: - virtual bool preVisit(QmlJS::AST::Node *node) - { - const char *name = typeid(*node).name(); -#ifdef Q_CC_GNU - name = abi::__cxa_demangle(name, 0, 0, 0) + 17; -#endif - std::cout << std::string(++depth, ' ') << name << std::endl; - return true; - } - - virtual void postVisit(QmlJS::AST::Node *) - { - --depth; - } -}; -#endif - -// Update the init instruction with final data, and optimize some simple -// bindings -bool QmlCompiler::finalizeComponent(int patch) +void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, + QmlParser::Property *prop, + QmlParser::Object *obj) { - for (int ii = 0; ii < compileState.bindings.count(); ++ii) { - const BindingReference &binding = compileState.bindings.at(ii); - finalizeBinding(binding); - } + Q_ASSERT(compileState.bindingMap.contains(binding)); - for (int ii = 0; ii < compileState.aliases.count(); ++ii) { - const AliasReference &alias = compileState.aliases.at(ii); - COMPILE_CHECK(finalizeAlias(alias)); + const BindingReference &ref = + compileState.bindings.at(compileState.bindingMap.value(binding)); + + QMetaProperty mp = obj->metaObject()->property(prop->index); + + QmlInstruction store; + int dataRef; + if (ref.compiledData.isEmpty()) { + dataRef = output->indexForString(ref.expression.asScript()); + store.type = QmlInstruction::StoreBinding; + } else { + dataRef = output->indexForByteArray(ref.compiledData); + store.type = QmlInstruction::StoreCompiledBinding; } - output->bytecode[patch].init.dataSize = compileState.savedObjects;; - output->bytecode[patch].init.bindingsSize = compileState.bindings.count(); - output->bytecode[patch].init.parserStatusSize = - compileState.parserStatusCount; + store.assignBinding.property = prop->index; + store.assignBinding.value = dataRef; + store.assignBinding.category = QmlMetaProperty::propertyCategory(mp); + store.assignBinding.context = ref.bindingContext.stack; - return true; + output->bytecode << store; } -bool QmlCompiler::finalizeAlias(const AliasReference &alias) +bool QmlCompiler::completeComponentBuild() { - COMPILE_CHECK(compileDynamicMeta(alias.object, alias.instructionIdx)); -} + saveComponentState(); + + for (int ii = 0; ii < compileState.aliases.count(); ++ii) { + const AliasReference &alias = compileState.aliases.at(ii); + COMPILE_CHECK(buildDynamicMeta(alias.object, ResolveAliases)); + } + -void QmlCompiler::finalizeBinding(const BindingReference &binding) -{ - QmlBasicScript bs; QmlBasicScript::Expression expr; expr.component = compileState.root; - expr.context = binding.bindingContext.object; - expr.property = binding.property; - expr.expression = binding.expression; - foreach (const IdReference &id, compileState.ids) + foreach (const IdReference &id, compileState.ids) { expr.ids.insert(id.id, qMakePair(id.object, id.idx)); + } - bs.compile(expr); - - QmlInstruction &instr = output->bytecode[binding.instructionIdx]; - instr.line = binding.value->location.start.line; - - // Single load optimization - if (bs.isValid() && bs.isSingleLoad()) { - - QString singleLoadTarget = QLatin1String(bs.singleLoadTarget()); - - if (singleLoadTarget.at(0).isUpper() && - compileState.ids.contains(singleLoadTarget) && - QmlMetaType::isObject(binding.property->type)) { - - IdReference reference = compileState.ids[singleLoadTarget]; - - const QMetaObject *idMo = reference.object->metaObject(); - const QMetaObject *storeMo = - QmlMetaType::rawMetaObjectForType(binding.property->type); - - Q_ASSERT(idMo); - Q_ASSERT(storeMo); - - bool canAssign = false; - while (!canAssign && idMo) { - if (idMo == storeMo) - canAssign = true; - else - idMo = idMo->superClass(); - } - - if (canAssign) { - int instructionIdx = reference.instructionIdx; - if (output->bytecode.at(instructionIdx).setId.save == -1) { - output->bytecode[instructionIdx].setId.save = - compileState.savedObjects++; - } - int saveId = output->bytecode.at(instructionIdx).setId.save; + for (int ii = 0; ii < compileState.bindings.count(); ++ii) { + BindingReference &binding = compileState.bindings[ii]; - instr.type = QmlInstruction::PushProperty; - instr.pushProperty.property = binding.property->index; - QmlInstruction store; - store.type = QmlInstruction::StoreStackObject; - store.line = 0; - store.assignStackObject.property = - compileState.pushedProperties++; - store.assignStackObject.object = saveId; - output->bytecode << store; - return; - } - } - } + QmlBasicScript bs; + expr.context = binding.bindingContext.object; + expr.property = binding.property; + expr.expression = binding.expression; - // General binding fallback - int bref; - if (bs.isValid()) { - bref = output->indexForByteArray(QByteArray(bs.compileData(), bs.compileDataSize())); - } else { - bref = output->indexForString(binding.expression.asScript()); + bs.compile(expr); + if (bs.isValid()) + binding.compiledData = + QByteArray(bs.compileData(), bs.compileDataSize()); } - instr.assignBinding.context = binding.bindingContext.stack; - - if (bs.isValid()) - instr.type = QmlInstruction::StoreCompiledBinding; - else - instr.type = QmlInstruction::StoreBinding; - - instr.assignBinding.property = binding.property->index; - instr.assignBinding.value = bref; - QMetaProperty mp = binding.property->parent->metaObject()->property(binding.property->index); - instr.assignBinding.category = QmlMetaProperty::propertyCategory(mp); + return true; } /*! - Returns true if object can be assigned to a (QObject) property of type - convertType. + Returns true if from can be assigned to a (QObject) property of type + to. */ -bool QmlCompiler::canConvert(int convertType, QmlParser::Object *object) +bool QmlCompiler::canCoerce(int to, QmlParser::Object *from) { - const QMetaObject *convertTypeMo = - QmlMetaType::rawMetaObjectForType(convertType); - const QMetaObject *objectMo = object->metaObject(); + const QMetaObject *toMo = + QmlMetaType::rawMetaObjectForType(to); + const QMetaObject *fromMo = from->metaObject(); - while (objectMo) { - if (objectMo == convertTypeMo) + while (fromMo) { + if (fromMo == toMo) return true; - objectMo = objectMo->superClass(); + fromMo = fromMo->superClass(); } return false; } +QmlType *QmlCompiler::toQmlType(QmlParser::Object *from) +{ + // ### Optimize + const QMetaObject *mo = from->metatype; + QmlType *type = 0; + while (!type && mo) { + type = QmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + return type; +} + QStringList QmlCompiler::deferredProperties(QmlParser::Object *obj) { const QMetaObject *mo = obj->metatype; @@ -1833,63 +2006,4 @@ QStringList QmlCompiler::deferredProperties(QmlParser::Object *obj) return rv; } -QmlCompiledData::QmlCompiledData() -{ -} - -QmlCompiledData::QmlCompiledData(const QmlCompiledData &other) -{ - *this = other; -} - -QmlCompiledData::~QmlCompiledData() -{ - for (int ii = 0; ii < types.count(); ++ii) { - if (types.at(ii).ref) - types.at(ii).ref->release(); - } -} - -QmlCompiledData &QmlCompiledData::operator=(const QmlCompiledData &other) -{ - types = other.types; - root = other.root; - primitives = other.primitives; - floatData = other.floatData; - intData = other.intData; - customTypeData = other.customTypeData; - datas = other.datas; - synthesizedMetaObjects = other.synthesizedMetaObjects; - bytecode = other.bytecode; - return *this; -} - -QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt) const -{ - if (type) { - QObject *rv = type->create(); - if (rv) - QmlEngine::setContextForObject(rv, ctxt); - return rv; - } else { - Q_ASSERT(component); - QObject *rv = component->create(ctxt); - QmlContext *ctxt = qmlContext(rv); - if(ctxt) { - static_cast<QmlContextPrivate *>(QObjectPrivate::get(ctxt))->typeName = className; - } - return rv; - } -} - -const QMetaObject *QmlCompiledData::TypeReference::metaObject() const -{ - if (type) { - return type->metaObject(); - } else { - Q_ASSERT(component); - return &static_cast<QmlComponentPrivate *>(QObjectPrivate::get(component))->cc->root; - } -} - QT_END_NAMESPACE |