summaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qmlcompiler.cpp
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-07-14 03:54:55 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-07-15 07:30:16 (GMT)
commit4f7e21dc04ce93531ef68f7694a5e8969448de8b (patch)
treece7e8170b5be6c974420d012a03f620f86cd6d8d /src/declarative/qml/qmlcompiler.cpp
parent9eca9e028884fb82d97e284826faa7965af356bd (diff)
downloadQt-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.cpp1728
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