X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fqml%2Fqml%2Fqqmlcompiler.cpp;h=6f2792570f58e8d672e5fb252f81472e6d197807;hb=49a3883e86b61d8facfeea9c43037d484cb50b92;hp=65247e1e808d8f92b0ee3d880fcbd2c6b4494e4a;hpb=b855240b782395f94315f43ea3e7e182299fac48;p=profile%2Fivi%2Fqtdeclarative.git diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 65247e1..6f27925 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -44,7 +44,6 @@ #include "qqmlpropertyvaluesource.h" #include "qqmlcomponent.h" #include -#include #include "qqmlstringconverters_p.h" #include "qqmlengine_p.h" #include "qqmlengine.h" @@ -63,7 +62,6 @@ #include "qqmlbinding_p.h" #include -#include #include #include #include @@ -71,12 +69,14 @@ #include #include #include +#include Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QJSValue) QT_BEGIN_NAMESPACE @@ -91,7 +91,7 @@ static QString id_string(QLatin1String("id")); static QString on_string(QLatin1String("on")); static QString Changed_string(QLatin1String("Changed")); static QString Component_string(QLatin1String("Component")); -static QString Component_import_string(QLatin1String("QML/Component")); +static QString Component_module_string(QLatin1String("QML")); static QString qsTr_string(QLatin1String("qsTr")); static QString qsTrId_string(QLatin1String("qsTrId")); @@ -99,7 +99,7 @@ static QString qsTrId_string(QLatin1String("qsTrId")); Instantiate a new QQmlCompiler. */ QQmlCompiler::QQmlCompiler(QQmlPool *pool) -: pool(pool), output(0), engine(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1), +: compileState(0), pool(pool), output(0), engine(0), enginePrivate(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1), cachedTranslationContextIndex(-1), componentStats(0) { if (compilerStatDump()) @@ -216,8 +216,7 @@ bool QQmlCompiler::isSignalPropertyName(const QHashedStringRef &name) This test corresponds to action taken by genLiteralAssignment(). Any change made here, must have a corresponding action in genLiteralAssigment(). */ -bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, - QQmlScript::Value *v) +bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, QQmlScript::Value *v) { const QQmlScript::Variant &value = v->value; @@ -225,7 +224,7 @@ bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); if (prop->core.isEnum()) { - QMetaProperty p = prop->parent->metaObject()->property(prop->index); + QMetaProperty p = prop->parent->metatype->firstCppMetaObject()->property(prop->index); int enumValue; bool ok; if (p.isFlagType()) { @@ -349,16 +348,16 @@ bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, break; case QVariant::Vector3D: { - bool ok; - QQmlStringConverters::vector3DFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); + QQmlInstruction::instr_storeVector3D::QVector3D v3; + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, value.asString(), &v3, sizeof(v3))) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); } break; case QVariant::Vector4D: { - bool ok; - QQmlStringConverters::vector4DFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected")); + QQmlInstruction::instr_storeVector4D::QVector4D v4; + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, value.asString(), &v4, sizeof(v4))) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected")); } break; default: @@ -395,26 +394,20 @@ bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected")); } break; + } else if (type == qMetaTypeId()) { + break; } // otherwise, check for existence of string converter to custom type QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type); if (!converter) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QVariant::typeToName((QVariant::Type)type)))); + COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(type)))); } break; } return true; } -static QUrl urlFromUserString(const QString &data) -{ - QUrl u; - // Preserve any valid percent-encoded octets supplied by the source - u.setEncodedUrl(data.toUtf8(), QUrl::TolerantMode); - return u; -} - /*! Generate a store instruction for assigning literal \a v to property \a prop. @@ -443,7 +436,7 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, if (v->value.isNumber()) { double n = v->value.asNumber(); if (double(int(n)) == n) { - if (prop->core.isVMEProperty()) { + if (prop->core.isVarProperty()) { Instruction::StoreVarInteger instr; instr.propertyIndex = prop->index; instr.value = int(n); @@ -455,7 +448,7 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, output->addInstruction(instr); } } else { - if (prop->core.isVMEProperty()) { + if (prop->core.isVarProperty()) { Instruction::StoreVarDouble instr; instr.propertyIndex = prop->index; instr.value = n; @@ -468,7 +461,7 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, } } } else if (v->value.isBoolean()) { - if (prop->core.isVMEProperty()) { + if (prop->core.isVarProperty()) { Instruction::StoreVarBool instr; instr.propertyIndex = prop->index; instr.value = v->value.asBoolean(); @@ -480,7 +473,7 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, output->addInstruction(instr); } } else { - if (prop->core.isVMEProperty()) { + if (prop->core.isVarProperty()) { Instruction::StoreVar instr; instr.propertyIndex = prop->index; instr.value = output->indexForString(v->value.asString()); @@ -522,7 +515,9 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, { Instruction::StoreUrl instr; QString string = v->value.asString(); - QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(urlFromUserString(string)); + // Encoded dir-separators defeat QUrl processing - decode them first + string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); + QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); instr.propertyIndex = prop->index; instr.value = output->indexForUrl(u); output->addInstruction(instr); @@ -563,9 +558,8 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, case QVariant::Color: { Instruction::StoreColor instr; - QColor c = QQmlStringConverters::colorFromString(v->value.asString()); instr.propertyIndex = prop->index; - instr.value = c.rgba(); + instr.value = QQmlStringConverters::rgbaFromString(v->value.asString()); output->addInstruction(instr); } break; @@ -684,25 +678,16 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, case QVariant::Vector3D: { Instruction::StoreVector3D instr; - bool ok; - QVector3D vector = QQmlStringConverters::vector3DFromString(v->value.asString(), &ok); instr.propertyIndex = prop->index; - instr.vector.xp = vector.x(); - instr.vector.yp = vector.y(); - instr.vector.zp = vector.z(); + QQmlStringConverters::createFromString(QMetaType::QVector3D, v->value.asString(), &instr.vector, sizeof(instr.vector)); output->addInstruction(instr); } break; case QVariant::Vector4D: { Instruction::StoreVector4D instr; - bool ok; - QVector4D vector = QQmlStringConverters::vector4DFromString(v->value.asString(), &ok); instr.propertyIndex = prop->index; - instr.vector.xp = vector.x(); - instr.vector.yp = vector.y(); - instr.vector.zp = vector.z(); - instr.vector.wp = vector.w(); + QQmlStringConverters::createFromString(QMetaType::QVector4D, v->value.asString(), &instr.vector, sizeof(instr.vector)); output->addInstruction(instr); } break; @@ -731,7 +716,7 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, } else if (type == qMetaTypeId >()) { Instruction::StoreUrlQList instr; QString string = v->value.asString(); - QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(urlFromUserString(string)); + QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); instr.propertyIndex = prop->index; instr.value = output->indexForUrl(u); output->addInstruction(instr); @@ -742,6 +727,32 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, instr.value = output->indexForString(v->value.asString()); output->addInstruction(instr); break; + } else if (type == qMetaTypeId()) { + if (v->value.isBoolean()) { + Instruction::StoreJSValueBool instr; + instr.propertyIndex = prop->index; + instr.value = v->value.asBoolean(); + output->addInstruction(instr); + } else if (v->value.isNumber()) { + double n = v->value.asNumber(); + if (double(int(n)) == n) { + Instruction::StoreJSValueInteger instr; + instr.propertyIndex = prop->index; + instr.value = int(n); + output->addInstruction(instr); + } else { + Instruction::StoreJSValueDouble instr; + instr.propertyIndex = prop->index; + instr.value = n; + output->addInstruction(instr); + } + } else { + Instruction::StoreJSValueString instr; + instr.propertyIndex = prop->index; + instr.value = output->indexForString(v->value.asString()); + output->addInstruction(instr); + } + break; } // otherwise, generate custom type literal assignment @@ -811,22 +822,23 @@ bool QQmlCompiler::compile(QQmlEngine *engine, QString err = ref.type->noCreationReason(); if (err.isEmpty()) err = tr( "Element is not creatable."); - COMPILE_EXCEPTION(parserRef->refObjects.first(), err); + COMPILE_EXCEPTION(parserRef->firstUse, err); } if (ref.type->containsRevisionedAttributes()) { QQmlError cacheError; - ref.typePropertyCache = enginePrivate->cache(ref.type, resolvedTypes.at(ii).minorVersion, + ref.typePropertyCache = enginePrivate->cache(ref.type, + resolvedTypes.at(ii).minorVersion, cacheError); if (!ref.typePropertyCache) - COMPILE_EXCEPTION(parserRef->refObjects.first(), cacheError.description()); + COMPILE_EXCEPTION(parserRef->firstUse, cacheError.description()); ref.typePropertyCache->addref(); } } else if (tref.typeData) { ref.component = tref.typeData->compiledData(); + ref.component->addref(); } - ref.className = parserRef->name; out->types << ref; } @@ -932,14 +944,17 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree) output->addInstruction(done); Q_ASSERT(tree->metatype); - - if (tree->metadata.isEmpty()) { - output->root = tree->metatype; + if (!tree->synthdata.isEmpty()) { + enginePrivate->registerCompositeType(output); + } else if (output->types.at(tree->type).component) { + output->metaTypeId = output->types.at(tree->type).component->metaTypeId; + output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId; } else { - static_cast(output->rootData) = *tree->metaObject(); - output->root = &output->rootData; + Q_ASSERT(output->types.at(tree->type).type); + output->metaTypeId = output->types.at(tree->type).type->typeId(); + output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId(); } - if (!tree->metadata.isEmpty()) + if (!tree->synthdata.isEmpty()) enginePrivate->registerCompositeType(output); } @@ -958,14 +973,11 @@ bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ct componentStats->componentStat.objects++; Q_ASSERT (obj->type != -1); - const QQmlCompiledData::TypeReference &tr = output->types.at(obj->type); - obj->metatype = tr.metaObject(); - - if (tr.type) - obj->typeName = tr.type->qmlTypeName(); + QQmlCompiledData::TypeReference &tr = output->types[obj->type]; + obj->metatype = tr.createPropertyCache(engine); - // This object is a "Component" element - if (tr.type && obj->metatype == &QQmlComponent::staticMetaObject) { + // This object is a "Component" element. + if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) { COMPILE_CHECK(buildComponent(obj, ctxt)); return true; } @@ -990,7 +1002,7 @@ bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ct // Create the synthesized meta object, ignoring aliases COMPILE_CHECK(checkDynamicMeta(obj)); COMPILE_CHECK(mergeDynamicMetaProperties(obj)); - COMPILE_CHECK(buildDynamicMeta(obj, IgnoreAliases)); + COMPILE_CHECK(buildDynamicMeta(obj, Normal)); // Find the native type and check for the QQmlParserStatus interface QQmlType *type = toQmlType(obj); @@ -1027,54 +1039,22 @@ bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ct Property *explicitProperty = 0; - const QMetaObject *mo = obj->metatype; - int idx = mo->indexOfClassInfo("DefaultProperty"); - if (idx != -1) { - QMetaClassInfo info = mo->classInfo(idx); - const char *p = info.value(); - if (p) { - int plen = 0; - char ord = 0; - while (char c = p[plen++]) { ord |= c; }; - --plen; - - if (ord & 0x80) { - // Utf8 - unoptimal, but seldom hit - QString *s = pool->NewString(QString::fromUtf8(p, plen)); - QHashedStringRef r(*s); - - if (obj->propertiesHashField.test(r.hash())) { - for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { - if (ep->name() == r) { - explicitProperty = ep; - break; - } - } - } - - if (!explicitProperty) - defaultProperty->setName(r); + QString defaultPropertyName = obj->metatype->defaultPropertyName(); + if (!defaultPropertyName.isEmpty()) { + QString *s = pool->NewString(defaultPropertyName); + QHashedStringRef r(*s); - } else { - QHashedCStringRef r(p, plen); - - if (obj->propertiesHashField.test(r.hash())) { - for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { - if (ep->name() == r) { - explicitProperty = ep; - break; - } - } - } - - if (!explicitProperty) { - // Set the default property name - QChar *buffer = pool->NewRawArray(r.length()); - r.writeUtf16(buffer); - defaultProperty->setName(QHashedStringRef(buffer, r.length(), r.hash())); + if (obj->propertiesHashField.test(r.hash())) { + for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { + if (ep->name() == r) { + explicitProperty = ep; + break; } } } + + if (!explicitProperty) + defaultProperty->setName(r); } if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) { @@ -1177,10 +1157,10 @@ bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ct return true; } -void QQmlCompiler::genObject(QQmlScript::Object *obj) +void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper) { QQmlCompiledData::TypeReference &tr = output->types[obj->type]; - if (tr.type && obj->metatype == &QQmlComponent::staticMetaObject) { + if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) { genComponent(obj); return; } @@ -1195,6 +1175,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj) create.type = obj->type; create.line = obj->location.start.line; create.column = obj->location.start.column; + create.parentToSuper = parentToSuper; output->addInstruction(create); } else { @@ -1208,6 +1189,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj) create.data = output->indexForByteArray(obj->custom); create.type = obj->type; create.isRoot = (compileState->root == obj); + create.parentToSuper = parentToSuper; output->addInstruction(create); } else { Instruction::CreateQMLObject create; @@ -1231,9 +1213,8 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj) } // Setup the synthesized meta object if necessary - if (!obj->metadata.isEmpty()) { + if (!obj->synthdata.isEmpty()) { Instruction::StoreMetaObject meta; - meta.data = output->indexForByteArray(obj->metadata); meta.aliasData = output->indexForByteArray(obj->synthdata); meta.propertyCache = output->propertyCaches.count(); @@ -1241,17 +1222,6 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj) Q_ASSERT(propertyCache); propertyCache->addref(); - // Add flag for alias properties - if (!obj->synthdata.isEmpty()) { - const QQmlVMEMetaData *vmeMetaData = - reinterpret_cast(obj->synthdata.constData()); - for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) { - int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii; - QQmlPropertyData *data = propertyCache->property(index); - data->setFlags(data->getFlags() | QQmlPropertyData::IsAlias); - } - } - if (obj == unitRoot) { propertyCache->addref(); output->rootPropertyCache = propertyCache; @@ -1379,11 +1349,14 @@ void QQmlCompiler::genObjectBody(QQmlScript::Object *obj) fetch.line = prop->location.start.line; output->addInstruction(fetch); - if (!prop->value->metadata.isEmpty()) { + if (!prop->value->synthdata.isEmpty()) { Instruction::StoreMetaObject meta; - meta.data = output->indexForByteArray(prop->value->metadata); meta.aliasData = output->indexForByteArray(prop->value->synthdata); - meta.propertyCache = -1; + meta.propertyCache = output->propertyCaches.count(); + QQmlPropertyCache *propertyCache = prop->value->synthCache; + Q_ASSERT(propertyCache); + propertyCache->addref(); + output->propertyCaches << propertyCache; output->addInstruction(meta); } @@ -1440,6 +1413,8 @@ void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Prop pop.type = prop->type; pop.bindingSkipList = 0; output->addInstruction(pop); + + genPropertyAssignment(prop, obj); } void QQmlCompiler::genComponent(QQmlScript::Object *obj) @@ -1624,7 +1599,7 @@ bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext int QQmlCompiler::componentTypeRef() { if (cachedComponentTypeRef == -1) { - QQmlType *t = QQmlMetaType::qmlType(Component_import_string,1,0); + QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0); for (int ii = output->types.count() - 1; ii >= 0; --ii) { if (output->types.at(ii).type == t) { cachedComponentTypeRef = ii; @@ -1632,7 +1607,6 @@ int QQmlCompiler::componentTypeRef() } } QQmlCompiledData::TypeReference ref; - ref.className = Component_string; ref.type = t; output->types << ref; cachedComponentTypeRef = output->types.count() - 1; @@ -1657,7 +1631,7 @@ int QQmlCompiler::translationContextIndex() bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj, const BindingContext &ctxt) { - Q_ASSERT(obj->metaObject()); + Q_ASSERT(obj->metatype); const QHashedStringRef &propName = prop->name(); @@ -1699,7 +1673,7 @@ bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *o if (prop->value || !prop->values.isOne()) COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment")); - prop->index = sig->coreIndex; + prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex); prop->core = *sig; obj->addSignalProperty(prop); @@ -1746,9 +1720,6 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, if (prop->isEmpty()) COMPILE_EXCEPTION(prop, tr("Empty property assignment")); - const QMetaObject *metaObject = obj->metaObject(); - Q_ASSERT(metaObject); - if (isAttachedPropertyName(prop->name())) { // Setup attached property data @@ -1760,8 +1731,8 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, } QQmlType *type = 0; - QQmlImportedNamespace *typeNamespace = 0; - unit->imports().resolveType(prop->name().toString(), &type, 0, 0, 0, &typeNamespace); + QQmlImportNamespace *typeNamespace = 0; + unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace); if (typeNamespace) { COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, @@ -1776,7 +1747,7 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, Q_ASSERT(type->attachedPropertiesFunction()); prop->index = type->attachedPropertiesId(); - prop->value->metatype = type->attachedPropertiesType(); + prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType()); } else { // Setup regular property data bool notInRevision = false; @@ -1795,13 +1766,13 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, prop->index = d->coreIndex; prop->core = *d; } else if (prop->isDefault) { - QMetaProperty p = QQmlMetaType::defaultProperty(metaObject); - QQmlPropertyData defaultPropertyData; - defaultPropertyData.load(p, engine); - if (p.name()) - prop->setName(QLatin1String(p.name())); - prop->core = defaultPropertyData; - prop->index = prop->core.coreIndex; + QString defaultPropertyName = obj->metatype->defaultPropertyName(); + + if (!defaultPropertyName.isEmpty()) { + prop->setName(defaultPropertyName); + prop->core = *obj->metatype->defaultProperty(); + prop->index = prop->core.coreIndex; + } } // We can't error here as the "id" property does not require a @@ -1867,10 +1838,10 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, return true; } -bool QQmlCompiler::buildPropertyInNamespace(QQmlImportedNamespace *ns, - QQmlScript::Property *nsProp, - QQmlScript::Object *obj, - const BindingContext &ctxt) +bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns, + QQmlScript::Property *nsProp, + QQmlScript::Object *obj, + const BindingContext &ctxt) { if (!nsProp->value) COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); @@ -1883,7 +1854,7 @@ bool QQmlCompiler::buildPropertyInNamespace(QQmlImportedNamespace *ns, // Setup attached property data QQmlType *type = 0; - unit->imports().resolveType(ns, prop->name().toString(), &type, 0, 0, 0); + unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0); if (!type || !type->attachedPropertiesType()) COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); @@ -1893,7 +1864,7 @@ bool QQmlCompiler::buildPropertyInNamespace(QQmlImportedNamespace *ns, Q_ASSERT(type->attachedPropertiesFunction()); prop->index = type->index(); - prop->value->metatype = type->attachedPropertiesType(); + prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType()); COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); } @@ -1971,7 +1942,7 @@ void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop, } else if (prop->type == QMetaType::QVariant) { - if (prop->core.isVMEProperty()) { + if (prop->core.isVarProperty()) { Instruction::StoreVarObject store; store.line = v->object->location.start.line; store.propertyIndex = prop->index; @@ -2010,31 +1981,25 @@ void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop, v->type == Value::ValueInterceptor); if (v->type == Value::ValueSource) { - genObject(v->object); + genObject(v->object, valueTypeProperty?true:false); Instruction::StoreValueSource store; - if (valueTypeProperty) { + if (valueTypeProperty) store.property = genValueTypeData(prop, valueTypeProperty); - store.owner = 1; - } else { + else store.property = prop->core; - store.owner = 0; - } QQmlType *valueType = toQmlType(v->object); store.castValue = valueType->propertyValueSourceCast(); output->addInstruction(store); } else if (v->type == Value::ValueInterceptor) { - genObject(v->object); + genObject(v->object, valueTypeProperty?true:false); Instruction::StoreValueInterceptor store; - if (valueTypeProperty) { + if (valueTypeProperty) store.property = genValueTypeData(prop, valueTypeProperty); - store.owner = 1; - } else { + else store.property = prop->core; - store.owner = 0; - } QQmlType *valueType = toQmlType(v->object); store.castValue = valueType->propertyValueInterceptorCast(); output->addInstruction(store); @@ -2143,10 +2108,15 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) { if (!prop->values.isEmpty()) { - if (prop->values.first()->location < prop->value->location) { - COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); - } else { - COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value")); + // Only error if we are assigning values, and not e.g. a property interceptor + for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) { + if (!dotProp->values.isEmpty()) { + if (prop->values.first()->location < prop->value->location) { + COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); + } else { + COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value")); + } + } } } @@ -2154,7 +2124,6 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); } - if (prop->isAlias) { for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) { vtProp->isAlias = true; @@ -2163,14 +2132,33 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], prop->value, obj, ctxt.incr())); + + // When building a value type where sub components are declared, this + // code path is followed from buildProperty, even if there is a previous + // assignment to the value type as a whole. Therefore we need to look + // for (and build) assignments to the entire value type before looking + // for any onValue assignments. + for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) { + if (v->object) { + COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types")); + } + COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); + } + + for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) { + Q_ASSERT(v->object); + COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt)); + } + obj->addValueTypeProperty(prop); + } else { COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); } } else { // Load the nested property's meta type - prop->value->metatype = enginePrivate->metaObjectForType(prop->type); + prop->value->metatype = enginePrivate->propertyCacheForType(prop->type); if (!prop->value->metatype) COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); @@ -2198,7 +2186,7 @@ bool QQmlCompiler::buildValueTypeProperty(QObject *type, if (obj->defaultProperty) COMPILE_EXCEPTION(obj, tr("Invalid property use")); - obj->metatype = type->metaObject(); + obj->metatype = enginePrivate->cache(type); for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { @@ -2227,7 +2215,7 @@ bool QQmlCompiler::buildValueTypeProperty(QObject *type, //optimization for . enum assignments bool isEnumAssignment = false; - if (prop->core.isEnum()) + if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment)); if (isEnumAssignment) { @@ -2388,22 +2376,22 @@ bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop, // meta object earlier to test for assignability. It doesn't matter // that there may still be outstanding synthesized meta object changes // on this type, as they are not relevant for assignability testing - v->object->metatype = output->types.at(v->object->type).metaObject(); - Q_ASSERT(v->object->metaObject()); + v->object->metatype = output->types[v->object->type].createPropertyCache(engine); + Q_ASSERT(v->object->metatype); // We want to raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type); + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(prop->type); // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; // Determine isAssignable value if (propertyMetaObject) { - const QMetaObject *c = v->object->metatype; - while(c) { - isAssignable |= (QQmlPropertyPrivate::equal(c, propertyMetaObject)); - c = c->superClass(); + QQmlPropertyCache *c = v->object->metatype; + while (c && !isAssignable) { + isAssignable |= c == propertyMetaObject; + c = c->parent(); } } @@ -2412,13 +2400,12 @@ bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop, COMPILE_CHECK(buildObject(v->object, ctxt)); v->type = Value::CreatedObject; - } else if (propertyMetaObject == &QQmlComponent::staticMetaObject) { + } else if (propertyMetaObject && propertyMetaObject->metaObject() == &QQmlComponent::staticMetaObject) { // Automatic "Component" insertion QQmlScript::Object *root = v->object; QQmlScript::Object *component = pool->New(); component->type = componentTypeRef(); - component->typeName = QStringLiteral("Qt/Component"); - component->metatype = &QQmlComponent::staticMetaObject; + component->metatype = enginePrivate->cache(&QQmlComponent::staticMetaObject); component->location = root->location; QQmlScript::Value *componentValue = pool->New(); componentValue->object = root; @@ -2458,8 +2445,8 @@ bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop, // meta object earlier to test for assignability. It doesn't matter // that there may still be outstanding synthesized meta object changes // on this type, as they are not relevant for assignability testing - v->object->metatype = output->types.at(v->object->type).metaObject(); - Q_ASSERT(v->object->metaObject()); + v->object->metatype = output->types[v->object->type].createPropertyCache(engine); + Q_ASSERT(v->object->metatype); // Will be true if the assigned type inherits QQmlPropertyValueSource bool isPropertyValue = false; @@ -2474,11 +2461,11 @@ bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop, // Assign as a property value source COMPILE_CHECK(buildObject(v->object, ctxt)); - if (isPropertyInterceptor && prop->parent->synthdata.isEmpty()) + if (isPropertyInterceptor && baseObj->synthdata.isEmpty()) buildDynamicMeta(baseObj, ForceCreation); v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor; } else { - COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(v->object->typeName).arg(prop->name().toString())); + COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(elementName(v->object)).arg(prop->name().toString())); } return true; @@ -2495,7 +2482,7 @@ bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop, if (v->value.isScript()) { //optimization for . enum assignments - if (prop->core.isEnum()) { + if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) { bool isEnumAssignment = false; COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment)); if (isEnumAssignment) { @@ -2520,16 +2507,23 @@ bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop, return true; } +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast (0)->staticQtMetaObject; } +}; + bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, QQmlScript::Object *obj, QQmlScript::Value *v, bool *isAssignment) { + bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum(); *isAssignment = false; - if (!prop->core.isEnum()) + if (!prop->core.isEnum() && !isIntProp) return true; - QMetaProperty mprop = obj->metaObject()->property(prop->index); + QMetaProperty mprop = obj->metatype->firstCppMetaObject()->property(prop->index); if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); @@ -2538,30 +2532,38 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, if (!string.at(0).isUpper()) return true; - QStringList parts = string.split(QLatin1Char('.')); - if (parts.count() != 2) + int dot = string.indexOf(QLatin1Char('.')); + if (dot == -1 || dot == string.length()-1) return true; - QString typeName = parts.at(0); - QQmlType *type = 0; - unit->imports().resolveType(typeName, &type, 0, 0, 0, 0); + if (string.indexOf(QLatin1Char('.'), dot+1) != -1) + return true; - //handle enums on value types (where obj->typeName is empty) - QString objTypeName = obj->typeName; - if (objTypeName.isEmpty()) { - QQmlType *objType = toQmlType(obj); - if (objType) - objTypeName = objType->qmlTypeName(); + QHashedStringRef typeName(string.constData(), dot); + QString enumValue = string.mid(dot+1); + + if (isIntProp) { + // Allow enum assignment to ints. + bool ok; + int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok); + if (ok) { + v->type = Value::Literal; + v->value = QQmlScript::Variant((double)enumval); + *isAssignment = true; + } + return true; } - if (!type) + QQmlType *type = 0; + unit->imports().resolveType(typeName, &type, 0, 0, 0, 0); + + if (!type && typeName != QLatin1String("Qt")) return true; - QString enumValue = parts.at(1); - int value; - bool ok; + int value = 0; + bool ok = false; - if (objTypeName == type->qmlTypeName()) { + if (type && toQmlType(obj) == type) { // When these two match, we can short cut the search if (mprop.isFlagType()) { value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); @@ -2570,13 +2572,15 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, } } else { // Otherwise we have to search the whole type - // This matches the logic in QV8TypeWrapper - QByteArray enumName = enumValue.toUtf8(); - const QMetaObject *metaObject = type->baseMetaObject(); - ok = false; - for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), &ok); + if (type) { + value = type->enumValue(QHashedStringRef(enumValue), &ok); + } else { + QByteArray enumName = enumValue.toUtf8(); + const QMetaObject *metaObject = StaticQtMetaObject::get(); + for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + value = e.keyToValue(enumName.constData(), &ok); + } } } @@ -2590,31 +2594,24 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, return true; } -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &static_cast (0)->staticQtMetaObject; } -}; - // Similar logic to above, but not knowing target property. -int QQmlCompiler::evaluateEnum(const QByteArray& script) const +int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const { - int dot = script.indexOf('.'); - if (dot > 0) { - const QByteArray &scope = script.left(dot); + Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer"); + *ok = false; + + if (scope != QLatin1String("Qt")) { QQmlType *type = 0; - unit->imports().resolveType(QString::fromUtf8(script.left(dot)), &type, 0, 0, 0, 0); - if (!type && scope != "Qt") - return -1; - const QMetaObject *mo = type ? type->metaObject() : StaticQtMetaObject::get(); - const char *key = script.constData() + dot+1; - int i = mo->enumeratorCount(); - while (i--) { - bool ok; - int v = mo->enumerator(i).keyToValue(key, &ok); - if (ok) - return v; - } + unit->imports().resolveType(scope, &type, 0, 0, 0, 0); + return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + } + + const QMetaObject *mo = StaticQtMetaObject::get(); + int i = mo->enumeratorCount(); + while (i--) { + int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); + if (*ok) + return v; } return -1; } @@ -2762,7 +2759,27 @@ bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj) return true; } -Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter) +#include + +static QStringList astNodeToStringList(QQmlJS::AST::Node *node) +{ + if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { + QString name = + static_cast(node)->name.toString(); + return QStringList() << name; + } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { + QQmlJS::AST::FieldMemberExpression *expr = static_cast(node); + + QStringList rv = astNodeToStringList(expr->base); + if (rv.isEmpty()) + return rv; + rv.append(expr->name.toString()); + return rv; + } + return QStringList(); +} + +static QAtomicInt classIndexCounter(0); bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode) { @@ -2775,569 +2792,542 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod obj->dynamicSlots.isEmpty()) return true; - bool resolveAlias = (mode == ResolveAliases); + Q_ASSERT(obj->synthCache == 0); + + struct TypeData { + Object::DynamicProperty::Type dtype; + int metaType; + } builtinTypes[] = { + { Object::DynamicProperty::Var, QMetaType::QVariant }, + { Object::DynamicProperty::Variant, QMetaType::QVariant }, + { Object::DynamicProperty::Int, QMetaType::Int }, + { Object::DynamicProperty::Bool, QMetaType::Bool }, + { Object::DynamicProperty::Real, QMetaType::Double }, + { Object::DynamicProperty::String, QMetaType::QString }, + { Object::DynamicProperty::Url, QMetaType::QUrl }, + { Object::DynamicProperty::Color, QMetaType::QColor }, + { Object::DynamicProperty::Time, QMetaType::QTime }, + { Object::DynamicProperty::Date, QMetaType::QDate }, + { Object::DynamicProperty::DateTime, QMetaType::QDateTime }, + { Object::DynamicProperty::Rect, QMetaType::QRectF }, + }; + static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (compileState->root == obj && !compileState->nested) { + QString path = output->url.path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(obj->metatype).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + QQmlPropertyCache *cache = obj->metatype->copyAndReserve(engine, obj->dynamicProperties.count(), + obj->dynamicProperties.count() + + obj->dynamicSignals.count() + + obj->dynamicSlots.count(), + obj->dynamicProperties.count() + + obj->dynamicSignals.count()); + + cache->_dynamicClassName = newClassName; + + int cStringNameCount = 0; - const Object::DynamicProperty *defaultProperty = 0; int aliasCount = 0; int varPropCount = 0; - int totalPropCount = 0; - int firstPropertyVarIndex = 0; - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { if (p->type == Object::DynamicProperty::Alias) aliasCount++; - if (p->type == Object::DynamicProperty::Var) + else if (p->type == Object::DynamicProperty::Var) varPropCount++; - if (p->isDefaultProperty && - (resolveAlias || p->type != Object::DynamicProperty::Alias)) - defaultProperty = p; + if (p->name.isLatin1()) { + p->nameIndex = cStringNameCount; + cStringNameCount += p->name.length() + 7 /* strlen("Changed") */; + } + + // No point doing this for both the alias and non alias cases + QQmlPropertyData *d = property(obj, p->name); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + } - if (!resolveAlias) { - // No point doing this for both the alias and non alias cases - QQmlPropertyData *d = property(obj, p->name); - if (d && d->isFinal()) - COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + if (s->name.isLatin1()) { + s->nameIndex = cStringNameCount; + cStringNameCount += s->name.length(); } } - bool buildData = resolveAlias || aliasCount == 0; + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + if (s->name.isLatin1()) { + s->nameIndex = cStringNameCount; + cStringNameCount += s->name.length(); + } + } - QByteArray dynamicData; - if (buildData) { - typedef QQmlVMEMetaData VMD; + char *cStringData = 0; + if (cStringNameCount) { + cache->_dynamicStringData.resize(cStringNameCount); + cStringData = cache->_dynamicStringData.data(); - dynamicData = QByteArray(sizeof(QQmlVMEMetaData) + - (obj->dynamicProperties.count() - aliasCount) * sizeof(VMD::PropertyData) + - obj->dynamicSlots.count() * sizeof(VMD::MethodData) + - aliasCount * sizeof(VMD::AliasData), 0); - } + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { - int uniqueClassId = classIndexCounter()->fetchAndAddRelaxed(1); + if (p->nameIndex == -1) continue; - QByteArray newClassName = obj->metatype->className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(uniqueClassId)); + char *myData = cStringData + p->nameIndex; + for (int ii = 0; ii < p->name.length(); ++ii) + *myData++ = p->name.at(ii).unicode(); + *myData++ = 'C'; *myData++ = 'h'; *myData++ = 'a'; *myData++ = 'n'; + *myData++ = 'g'; *myData++ = 'e'; *myData++ = 'd'; + } - if (compileState->root == obj && !compileState->nested) { - QString path = output->url.path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + QByteArray::number(uniqueClassId); + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + + if (s->nameIndex == -1) continue; + + char *myData = cStringData + s->nameIndex; + for (int ii = 0; ii < s->name.length(); ++ii) + *myData++ = s->name.at(ii).unicode(); + } + + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; + s = obj->dynamicSignals.next(s)) { + + if (s->nameIndex == -1) continue; + + char *myData = cStringData + s->nameIndex; + for (int ii = 0; ii < s->name.length(); ++ii) + *myData++ = s->name.at(ii).unicode(); } } - QFastMetaBuilder builder; - QFastMetaBuilder::StringRef classNameRef = builder.init(newClassName.length(), - obj->dynamicProperties.count() - (resolveAlias?0:aliasCount), - obj->dynamicSlots.count(), - obj->dynamicSignals.count() + obj->dynamicProperties.count(), - defaultProperty?1:0); + QByteArray dynamicData; + typedef QQmlVMEMetaData VMD; - struct TypeData { - Object::DynamicProperty::Type dtype; - int metaType; - const char *cppType; - } builtinTypes[] = { - { Object::DynamicProperty::Var, QMetaType::QVariant, "QVariant" }, - { Object::DynamicProperty::Variant, QMetaType::QVariant, "QVariant" }, - { Object::DynamicProperty::Int, QMetaType::Int, "int" }, - { Object::DynamicProperty::Bool, QMetaType::Bool, "bool" }, - { Object::DynamicProperty::Real, QMetaType::Double, "double" }, - { Object::DynamicProperty::String, QMetaType::QString, "QString" }, - { Object::DynamicProperty::Url, QMetaType::QUrl, "QUrl" }, - { Object::DynamicProperty::Color, QMetaType::QColor, "QColor" }, - { Object::DynamicProperty::Time, QMetaType::QTime, "QTime" }, - { Object::DynamicProperty::Date, QMetaType::QDate, "QDate" }, - { Object::DynamicProperty::DateTime, QMetaType::QDateTime, "QDateTime" }, - }; - static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - QFastMetaBuilder::StringRef typeRefs[builtinTypeCount]; + dynamicData = QByteArray(sizeof(QQmlVMEMetaData) + + obj->dynamicProperties.count() * sizeof(VMD::PropertyData) + + obj->dynamicSlots.count() * sizeof(VMD::MethodData) + + aliasCount * sizeof(VMD::AliasData), 0); - // Reserve dynamic properties - if (obj->dynamicProperties.count()) { - typedef QQmlVMEMetaData VMD; + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; - int effectivePropertyIndex = 0; - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + // First set up notify signals for properties - first normal, then var, then alias + enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 }; + for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias - // Reserve space for name - p->nameRef = builder.newString(p->name.utf8length()); + if (ii == NSS_Var && varPropCount == 0) continue; + else if (ii == NSS_Alias && aliasCount == 0) continue; - int propertyType = 0; - bool readonly = false; - QFastMetaBuilder::StringRef typeRef; + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { - if (p->type == Object::DynamicProperty::Alias) { + if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias || + p->type == Object::DynamicProperty::Var)) || + ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) || + ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias))) continue; - } else if (p->type < builtinTypeCount) { - Q_ASSERT(builtinTypes[p->type].dtype == p->type); - propertyType = builtinTypes[p->type].metaType; - if (typeRefs[p->type].isEmpty()) - typeRefs[p->type] = builder.newString(strlen(builtinTypes[p->type].cppType)); - typeRef = typeRefs[p->type]; - } else { - Q_ASSERT(p->type == Object::DynamicProperty::CustomList || - p->type == Object::DynamicProperty::Custom); - - // XXX don't double resolve this in the case of an alias run - - QByteArray customTypeName; - QQmlType *qmltype = 0; - QString url; - if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0)) - COMPILE_EXCEPTION(p, tr("Invalid property type")); - - if (!qmltype) { - QQmlTypeData *tdata = enginePrivate->typeLoader.get(QUrl(url)); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - QQmlCompiledData *data = tdata->compiledData(); - customTypeName = data->root->className(); - data->release(); - tdata->release(); - } else { - customTypeName = qmltype->typeName(); - } + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; - if (p->type == Object::DynamicProperty::Custom) { - customTypeName += '*'; - propertyType = QMetaType::QObjectStar; - } else { - readonly = true; - customTypeName = QByteArray("QQmlListProperty<") + customTypeName + QByteArray(">"); - propertyType = qMetaTypeId >(); - } + if (p->nameIndex != -1) { + QHashedCStringRef changedSignalName(cStringData + p->nameIndex, + p->name.length() + 7 /* strlen("Changed") */); + cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++); + } else { + QString changedSignalName = p->name.toString() + QLatin1String("Changed"); - p->resolvedCustomTypeName = pool->NewByteArray(customTypeName); - p->typeRef = builder.newString(customTypeName.length()); - typeRef = p->typeRef; + cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++); } + } + } - if (p->type == Object::DynamicProperty::Var) - continue; - - if (p->isReadOnly) - readonly = true; - - if (buildData) { - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - vmd->propertyCount++; - (vmd->propertyData() + effectivePropertyIndex)->propertyType = propertyType; - } - - if (p->type < builtinTypeCount) - builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, (QMetaType::Type)propertyType, - readonly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, - effectivePropertyIndex); - else - builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, - readonly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, - effectivePropertyIndex); - - p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); - builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); - - effectivePropertyIndex++; - } - - if (varPropCount) { - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - if (buildData) - vmd->varPropertyCount = varPropCount; - firstPropertyVarIndex = effectivePropertyIndex; - totalPropCount = varPropCount + effectivePropertyIndex; - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { - if (p->type == Object::DynamicProperty::Var) { - QFastMetaBuilder::StringRef typeRef = typeRefs[p->type]; - if (buildData) { - vmd->propertyCount++; - (vmd->propertyData() + effectivePropertyIndex)->propertyType = QMetaType::QVariant; - } + // Dynamic signals + for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + int paramCount = s->parameterNames.count(); - builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, - QMetaType::QVariant, - p->isReadOnly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, - effectivePropertyIndex); + QList names; + QVarLengthArray paramTypes(paramCount?(paramCount + 1):0); - p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); - builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); + if (paramCount) { + paramTypes[0] = paramCount; - effectivePropertyIndex++; - } + for (int i = 0; i < paramCount; ++i) { + Q_ASSERT(s->parameterTypes.at(i) < builtinTypeCount); + paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType; + names.append(s->parameterNames.at(i).toString().toUtf8()); } } - - if (aliasCount) { - int aliasIndex = 0; - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { - if (p->type == Object::DynamicProperty::Alias) { - if (resolveAlias) { - Q_ASSERT(buildData); - ((QQmlVMEMetaData *)dynamicData.data())->aliasCount++; - COMPILE_CHECK(compileAlias(builder, dynamicData, obj, effectivePropertyIndex, - aliasIndex, *p)); - } - // Even if we aren't resolving the alias, we need a fake signal so that the - // metaobject remains consistent across the resolve and non-resolve alias runs - p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); - builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); - effectivePropertyIndex++; - aliasIndex++; - } - } + + ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + if (s->nameIndex != -1) { + QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash()); + cache->appendSignal(name, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } else { + QString name = s->name.toString(); + cache->appendSignal(name, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); } } - // Reserve default property - QFastMetaBuilder::StringRef defPropRef; - if (defaultProperty) { - defPropRef = builder.newString(strlen("DefaultProperty")); - builder.setClassInfo(0, defPropRef, defaultProperty->nameRef); - } - - // Reserve dynamic signals - int signalIndex = 0; - for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + // Dynamic slots + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { int paramCount = s->parameterNames.count(); - int signatureSize = s->name.utf8length() + 2 /* paren */; - int namesSize = 0; - if (paramCount) signatureSize += s->parameterTypesLength() + (paramCount - 1) /* commas */; - if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1) /* commas */; + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - s->signatureRef = builder.newString(signatureSize); - if (namesSize) s->parameterNamesRef = builder.newString(namesSize); + if (paramCount) + flags |= QQmlPropertyData::HasArguments; - if (buildData) - ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; - - builder.setSignal(signalIndex + obj->dynamicProperties.count(), s->signatureRef, s->parameterNamesRef); - ++signalIndex; + if (s->nameIndex != -1) { + QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash()); + cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames); + } else { + QString name = s->name.toString(); + cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames); + } } - // Reserve dynamic slots - if (obj->dynamicSlots.count()) { - // Allocate QVariant string - if (typeRefs[0].isEmpty()) - typeRefs[0] = builder.newString(strlen(builtinTypes[0].cppType)); + // Dynamic properties (except var and aliases) + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { - typedef QQmlVMEMetaData VMD; + if (p->type == Object::DynamicProperty::Alias || + p->type == Object::DynamicProperty::Var) + continue; - int methodIndex = 0; - for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - int paramCount = s->parameterNames.count(); - - int signatureSize = s->name.utf8length() + 2 /* paren */; - int namesSize = 0; - if (paramCount) signatureSize += (paramCount * strlen("QVariant") + (paramCount - 1)); - if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */); - - s->signatureRef = builder.newString(signatureSize); - if (namesSize) s->parameterNamesRef = builder.newString(namesSize); - - builder.setMethod(methodIndex, s->signatureRef, s->parameterNamesRef, typeRefs[0]); - - if (buildData) { - QString funcScript; - funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ + - namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */); - funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('('); - for (int jj = 0; jj < paramCount; ++jj) { - if (jj) funcScript.append(QLatin1Char(',')); - funcScript.append(QLatin1String(s->parameterNames.at(jj))); - } - funcScript += QLatin1Char(')') + s->body + QLatin1Char(')'); + int propertyType = 0; + int vmePropertyType = 0; + quint32 propertyFlags = 0; - QByteArray utf8 = funcScript.toUtf8(); - VMD::MethodData methodData = { s->parameterNames.count(), 0, - utf8.length(), - s->location.start.line }; + if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + vmePropertyType = propertyType; - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - vmd->methodCount++; + if (p->type == Object::DynamicProperty::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == Object::DynamicProperty::CustomList || + p->type == Object::DynamicProperty::Custom); - VMD::MethodData &md = *(vmd->methodData() + methodIndex); - md = methodData; - md.bodyOffset = dynamicData.size(); + QQmlType *qmltype = 0; + QString url; + if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0)) + COMPILE_EXCEPTION(p, tr("Invalid property type")); - dynamicData.append((const char *)utf8.constData(), utf8.length()); - } + if (!qmltype) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url)); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + QQmlCompiledData *data = tdata->compiledData(); - methodIndex++; - } - } + if (p->type == Object::DynamicProperty::Custom) { + propertyType = data->metaTypeId; + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = data->listMetaTypeId; + vmePropertyType = qMetaTypeId >(); + } - // Now allocate used builtin types - for (int ii = 0; ii < builtinTypeCount; ++ii) { - if (!typeRefs[ii].isEmpty()) - typeRefs[ii].load(builtinTypes[ii].cppType); - } + tdata->release(); + } else { + if (p->type == Object::DynamicProperty::Custom) { + propertyType = qmltype->typeId(); + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = qmltype->qListTypeId(); + vmePropertyType = qMetaTypeId >(); + } + } - // Now allocate properties - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + if (p->type == Object::DynamicProperty::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } - char *d = p->changedSignatureRef.data(); - p->name.writeUtf8(d); - strcpy(d + p->name.utf8length(), "Changed()"); + if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; - if (p->type == Object::DynamicProperty::Alias && !resolveAlias) - continue; + if (p->nameIndex != -1) { + QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), + p->name.hash()); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + } else { + QString propertyName = p->name.toString(); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + } - p->nameRef.load(p->name); + effectiveSignalIndex++; - if (p->type >= builtinTypeCount) { - Q_ASSERT(p->resolvedCustomTypeName); - p->typeRef.load(*p->resolvedCustomTypeName); - } + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType; + vmd->propertyCount++; } - // Allocate default property if necessary - if (defaultProperty) - strcpy(defPropRef.data(), "DefaultProperty"); - - // Now allocate signals - for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { + // Now do var properties + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount; + p = obj->dynamicProperties.next(p)) { - char *d = s->signatureRef.data(); - char *d2 = s->parameterNamesRef.isEmpty()?0:s->parameterNamesRef.data(); - s->name.writeUtf8(d); d += s->name.utf8length(); - *d++ = '('; + if (p->type != Object::DynamicProperty::Var) + continue; - for (int jj = 0; jj < s->parameterNames.count(); ++jj) { - if (jj != 0) { *d++ = ','; *d2++ = ','; } - strcpy(d, s->parameterTypes.at(jj).constData()); - d += s->parameterTypes.at(jj).length(); - s->parameterNames.at(jj).writeUtf8(d2); - d2 += s->parameterNames.at(jj).utf8length(); + quint32 propertyFlags = QQmlPropertyData::IsVarProperty; + if (!p->isReadOnly) + propertyFlags |= QQmlPropertyData::IsWritable; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant; + vmd->propertyCount++; + ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++; + + if (p->nameIndex != -1) { + QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), + p->name.hash()); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + QMetaType::QVariant, effectiveSignalIndex); + } else { + QString propertyName = p->name.toString(); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + QMetaType::QVariant, effectiveSignalIndex); } - *d++ = ')'; - *d = 0; - if (d2) *d2 = 0; - } - // Now allocate methods - for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - char *d = s->signatureRef.data(); - char *d2 = s->parameterNamesRef.isEmpty()?0:s->parameterNamesRef.data(); - s->name.writeUtf8(d); d += s->name.utf8length(); - *d++ = '('; - for (int jj = 0; jj < s->parameterNames.count(); ++jj) { - if (jj != 0) { *d++ = ','; *d2++ = ','; } - strcpy(d, "QVariant"); - d += strlen("QVariant"); - strcpy(d2, s->parameterNames.at(jj).constData()); - d2 += s->parameterNames.at(jj).length(); - } - *d++ = ')'; - *d = 0; - if (d2) *d2 = 0; + effectiveSignalIndex++; } - // Now allocate class name - classNameRef.load(newClassName); + // Alias property count. Actual data is setup in buildDynamicMetaAliases + ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; + + // Dynamic slot data - comes after the property data + for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { + int paramCount = s->parameterNames.count(); - obj->metadata = builder.toData(); - builder.fromData(&obj->extObject, obj->metatype, obj->metadata); + QString funcScript; + int namesSize = 0; + if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */); + funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ + + namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */); + funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('('); + for (int jj = 0; jj < paramCount; ++jj) { + if (jj) funcScript.append(QLatin1Char(',')); + funcScript.append(QLatin1String(s->parameterNames.at(jj))); + } + funcScript += QLatin1Char(')') + s->body + QLatin1Char(')'); - if (mode == IgnoreAliases && aliasCount) - compileState->aliasingObjects.append(obj); + QByteArray utf8 = funcScript.toUtf8(); + VMD::MethodData methodData = { s->parameterNames.count(), + dynamicData.size(), + utf8.length(), + s->location.start.line }; - obj->synthdata = dynamicData; + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); + vmd->methodCount++; + md = methodData; - if (obj->synthCache) { - obj->synthCache->release(); - obj->synthCache = 0; + dynamicData.append((const char *)utf8.constData(), utf8.length()); } - if (obj->type != -1) { - QQmlPropertyCache *superCache = output->types[obj->type].createPropertyCache(engine); - QQmlPropertyCache *cache = - superCache->copyAndAppend(engine, &obj->extObject, - QQmlPropertyData::NoFlags, - QQmlPropertyData::IsVMEFunction, - QQmlPropertyData::IsVMESignal); - - // now we modify the flags appropriately for var properties. - int propertyOffset = obj->extObject.propertyOffset(); - QQmlPropertyData *currPropData = 0; - for (int pvi = firstPropertyVarIndex; pvi < totalPropCount; ++pvi) { - currPropData = cache->property(pvi + propertyOffset); - currPropData->setFlags(currPropData->getFlags() | QQmlPropertyData::IsVMEProperty); - } + if (aliasCount) + compileState->aliasingObjects.append(obj); - obj->synthCache = cache; - } + obj->synthdata = dynamicData; + obj->synthCache = cache; + obj->metatype = cache; return true; } -bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val) +bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) { - if (val.isEmpty()) - COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); + Q_ASSERT(obj->synthCache); - QChar ch = val.at(0); - if (ch.isLetter() && !ch.isLower()) - COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); + QByteArray &dynamicData = obj->synthdata; - QChar u(QLatin1Char('_')); - if (!ch.isLetter() && ch != u) - COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); + QQmlPropertyCache *cache = obj->synthCache; + char *cStringData = cache->_dynamicStringData.data(); - for (int ii = 1; ii < val.count(); ++ii) { - ch = val.at(ii); - if (!ch.isLetterOrNumber() && ch != u) - COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); - } + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count(); + int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count(); + int effectiveAliasIndex = 0; - if (enginePrivate->v8engine()->illegalNames().contains(val)) - COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { - return true; -} + if (p->type != Object::DynamicProperty::Alias) + continue; -#include + if (!p->defaultValue) + COMPILE_EXCEPTION(obj, tr("No property alias location")); -static QStringList astNodeToStringList(QQmlJS::AST::Node *node) -{ - if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { - QString name = - static_cast(node)->name.toString(); - return QStringList() << name; - } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { - QQmlJS::AST::FieldMemberExpression *expr = static_cast(node); + if (!p->defaultValue->values.isOne() || + p->defaultValue->values.first()->object || + !p->defaultValue->values.first()->value.isScript()) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - QStringList rv = astNodeToStringList(expr->base); - if (rv.isEmpty()) - return rv; - rv.append(expr->name.toString()); - return rv; - } - return QStringList(); -} + QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST(); + Q_ASSERT(node); -bool QQmlCompiler::compileAlias(QFastMetaBuilder &builder, - QByteArray &data, - QQmlScript::Object *obj, - int propIndex, int aliasIndex, - Object::DynamicProperty &prop) -{ - if (!prop.defaultValue) - COMPILE_EXCEPTION(obj, tr("No property alias location")); + QStringList alias = astNodeToStringList(node); + if (alias.count() < 1 || alias.count() > 3) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as , . or ..")); - if (!prop.defaultValue->values.isOne() || - prop.defaultValue->values.first()->object || - !prop.defaultValue->values.first()->value.isScript()) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + QQmlScript::Object *idObject = compileState->ids.value(alias.at(0)); + if (!idObject) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); - QQmlJS::AST::Node *node = prop.defaultValue->values.first()->value.asAST(); - if (!node) - COMPILE_EXCEPTION(obj, tr("No property alias location")); // ### Can this happen? + int propIdx = -1; + int notifySignal = -1; + int flags = 0; + int type = 0; + bool writable = false; + bool resettable = false; - QStringList alias = astNodeToStringList(node); + quint32 propertyFlags = QQmlPropertyData::IsAlias; - if (alias.count() < 1 || alias.count() > 3) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. An alias reference must be specified as , . or ..")); + if (alias.count() == 2 || alias.count() == 3) { + QQmlPropertyData *property = this->property(idObject, alias.at(1)); - QQmlScript::Object *idObject = compileState->ids.value(alias.at(0)); - if (!idObject) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); + if (!property || property->coreIndex > 0xFFFF) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - QByteArray typeName; + propIdx = property->coreIndex; + type = property->propType; - int propIdx = -1; - int flags = 0; - int type = 0; - bool writable = false; - bool resettable = false; - if (alias.count() == 2 || alias.count() == 3) { - propIdx = indexOfProperty(idObject, alias.at(1)); + writable = property->isWritable(); + resettable = property->isResettable(); + notifySignal = property->notifyIndex; - if (-1 == propIdx) { - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); - } else if (propIdx > 0xFFFF) { - COMPILE_EXCEPTION(prop.defaultValue, tr("Alias property exceeds alias bounds")); - } + if (alias.count() == 3) { + QQmlValueType *valueType = enginePrivate->valueTypes[type]; // XXX threadsafe? + if (!valueType) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - QMetaProperty aliasProperty = idObject->metaObject()->property(propIdx); - if (!aliasProperty.isScriptable()) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + propIdx |= ((unsigned int)type) << 24; + int valueTypeIndex = + valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); + if (valueTypeIndex == -1) + COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); + Q_ASSERT(valueTypeIndex <= 0xFF); - writable = aliasProperty.isWritable() && !prop.isReadOnly; - resettable = aliasProperty.isResettable() && !prop.isReadOnly; + propIdx |= (valueTypeIndex << 16); + if (valueType->metaObject()->property(valueTypeIndex).isEnumType()) + type = QVariant::Int; + else + type = valueType->metaObject()->property(valueTypeIndex).userType(); - if (aliasProperty.type() < QVariant::UserType - || uint(aliasProperty.type()) == QMetaType::QVariant) - type = aliasProperty.type(); + } else { + if (property->isEnum()) { + type = QVariant::Int; + } else { + // Copy type flags + propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask; - if (alias.count() == 3) { - QQmlValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()]; - if (!valueType) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); + if (property->isVarProperty()) + propertyFlags |= QQmlPropertyData::IsQVariant; - propIdx |= ((unsigned int)aliasProperty.type()) << 24; + if (property->isQObject()) + flags |= QML_ALIAS_FLAG_PTR; + } + } + } else { + Q_ASSERT(idObject->type != -1); // How else did it get an id? - int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); - if (valueTypeIndex == -1) - COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); - Q_ASSERT(valueTypeIndex <= 0xFF); - - aliasProperty = valueType->metaObject()->property(valueTypeIndex); - propIdx |= (valueTypeIndex << 16); + const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type); + if (ref.type) + type = ref.type->typeId(); + else + type = ref.component->metaTypeId; - // update the property type - type = aliasProperty.type(); - if (type >= (int)QVariant::UserType) - type = 0; + flags |= QML_ALIAS_FLAG_PTR; + propertyFlags |= QQmlPropertyData::IsQObjectDerived; } - if (aliasProperty.isEnumType()) - typeName = "int"; // Avoid introducing a dependency on the aliased metaobject - else - typeName = aliasProperty.typeName(); - } else { - Q_ASSERT(idObject->type != -1); // How else did it get an id? + QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags, notifySignal }; + + typedef QQmlVMEMetaData VMD; + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + *(vmd->aliasData() + effectiveAliasIndex++) = aliasData; - const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type); - if (ref.type) - typeName = ref.type->typeName(); + if (!p->isReadOnly && writable) + propertyFlags |= QQmlPropertyData::IsWritable; else - typeName = ref.component->root->className(); + propertyFlags &= ~QQmlPropertyData::IsWritable; - typeName += '*'; + if (resettable) + propertyFlags |= QQmlPropertyData::IsResettable; + else + propertyFlags &= ~QQmlPropertyData::IsResettable; + + if (p->nameIndex != -1) { + QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), + p->name.hash()); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + } else { + QString propertyName = p->name.toString(); + if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + } } - if (typeName.endsWith('*')) - flags |= QML_ALIAS_FLAG_PTR; + return true; +} - QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags }; +bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val) +{ + if (val.isEmpty()) + COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); - typedef QQmlVMEMetaData VMD; - VMD *vmd = (QQmlVMEMetaData *)data.data(); - *(vmd->aliasData() + aliasIndex) = aliasData; + QChar ch = val.at(0); + if (ch.isLetter() && !ch.isLower()) + COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); - prop.nameRef = builder.newString(prop.name.utf8length()); - prop.resolvedCustomTypeName = pool->NewByteArray(typeName); - prop.typeRef = builder.newString(typeName.length()); + QChar u(QLatin1Char('_')); + if (!ch.isLetter() && ch != u) + COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); - int propertyFlags = 0; - if (writable) - propertyFlags |= QFastMetaBuilder::Writable; - if (resettable) - propertyFlags |= QFastMetaBuilder::Resettable; + for (int ii = 1; ii < val.count(); ++ii) { + ch = val.at(ii); + if (!ch.isLetterOrNumber() && ch != u) + COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); + } - builder.setProperty(propIndex, prop.nameRef, prop.typeRef, (QMetaType::Type)type, - (QFastMetaBuilder::PropertyFlag)propertyFlags, - propIndex); + if (enginePrivate->v8engine()->illegalNames().contains(val)) + COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); return true; } @@ -3348,7 +3338,7 @@ bool QQmlCompiler::buildBinding(QQmlScript::Value *value, { Q_ASSERT(prop->index != -1); Q_ASSERT(prop->parent); - Q_ASSERT(prop->parent->metaObject()); + Q_ASSERT(prop->parent->metatype); if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration) COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); @@ -3468,8 +3458,10 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, Instruction::StoreV4Binding store; store.value = js.compiledIndex; + store.fallbackValue = js.sharedIndex; store.context = js.bindingContext.stack; store.owner = js.bindingContext.owner; + store.isAlias = prop->isAlias; if (valueTypeProperty) { store.property = (valueTypeProperty->index & 0xFFFF) | ((valueTypeProperty->type & 0xFF)) << 16 | @@ -3482,18 +3474,27 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, store.line = binding->location.start.line; store.column = binding->location.start.column; output->addInstruction(store); + + if (store.fallbackValue > -1) { + //also create v8 instruction (needed to properly configure the fallback v8 binding) + JSBindingReference &js = static_cast(*binding->bindingReference); + js.dataType = BindingReference::V8; + genBindingAssignment(binding, prop, obj, valueTypeProperty); + } } else if (ref.dataType == BindingReference::V8) { const JSBindingReference &js = static_cast(ref); Instruction::StoreV8Binding store; - store.value = js.compiledIndex; + store.value = js.sharedIndex; store.context = js.bindingContext.stack; store.owner = js.bindingContext.owner; + store.isAlias = prop->isAlias; if (valueTypeProperty) { store.isRoot = (compileState->root == valueTypeProperty->parent); } else { store.isRoot = (compileState->root == obj); } + store.isFallback = js.compiledIndex > -1; store.line = binding->location.start.line; store.column = binding->location.start.column; @@ -3509,30 +3510,30 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, } else if (ref.dataType == BindingReference::QtScript) { const JSBindingReference &js = static_cast(ref); - QQmlInstruction store; - store.assignBinding.value = output->indexForString(js.rewrittenExpression); - store.assignBinding.context = js.bindingContext.stack; - store.assignBinding.owner = js.bindingContext.owner; - store.assignBinding.line = binding->location.start.line; - store.assignBinding.column = binding->location.start.column; + Instruction::StoreBinding store; + store.value = output->indexForString(js.rewrittenExpression); + store.context = js.bindingContext.stack; + store.owner = js.bindingContext.owner; + store.line = binding->location.start.line; + store.column = binding->location.start.column; + store.isAlias = prop->isAlias; if (valueTypeProperty) { - store.assignBinding.isRoot = (compileState->root == valueTypeProperty->parent); + store.isRoot = (compileState->root == valueTypeProperty->parent); } else { - store.assignBinding.isRoot = (compileState->root == obj); + store.isRoot = (compileState->root == obj); } + store.isFallback = false; Q_ASSERT(js.bindingContext.owner == 0 || (js.bindingContext.owner != 0 && valueTypeProperty)); if (js.bindingContext.owner) { - store.assignBinding.property = genValueTypeData(prop, valueTypeProperty); + store.property = genValueTypeData(prop, valueTypeProperty); } else { - store.assignBinding.property = prop->core; + store.property = prop->core; } - output->addInstructionHelper( - !prop->isAlias ? QQmlInstruction::StoreBinding - : QQmlInstruction::StoreBindingOnAlias - , store); + + output->addInstruction(store); } else { Q_ASSERT(!"Unhandled BindingReference::DataType type"); } @@ -3557,8 +3558,7 @@ QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp, QQmlScript::Property *prop) { typedef QQmlPropertyPrivate QDPP; - return QDPP::saveValueType(prop->parent->metaObject(), prop->index, - enginePrivate->valueTypes[prop->type]->metaObject(), + return QDPP::saveValueType(prop->core, enginePrivate->valueTypes[prop->type]->metaObject(), valueTypeProp->index, engine); } @@ -3569,7 +3569,7 @@ bool QQmlCompiler::completeComponentBuild() for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject; aliasObject = compileState->aliasingObjects.next(aliasObject)) - COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases)); + COMPILE_CHECK(buildDynamicMetaAliases(aliasObject)); QV4Compiler::Expression expr(unit->imports()); expr.component = compileState->root; @@ -3584,21 +3584,27 @@ bool QQmlCompiler::completeComponentBuild() JSBindingReference &binding = *b; - // ### We don't currently optimize for bindings on alias's - because - // of the solution to QTBUG-13719 - if (!binding.property->isAlias) { - expr.context = binding.bindingContext.object; - expr.property = binding.property; - expr.expression = binding.expression; - - int index = bindingCompiler.compile(expr, enginePrivate); - if (index != -1) { - binding.dataType = BindingReference::V4; - binding.compiledIndex = index; - if (componentStats) - componentStats->componentStat.optimizedBindings.append(b->value->location); + // First try v4 + expr.context = binding.bindingContext.object; + expr.property = binding.property; + expr.expression = binding.expression; + + bool needsFallback = false; + int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback); + if (index != -1) { + // Ensure the index value fits within the available space + Q_ASSERT(index < (1 << 15)); + + binding.dataType = BindingReference::V4; + binding.compiledIndex = index; + binding.sharedIndex = -1; + if (componentStats) + componentStats->componentStat.optimizedBindings.append(b->value->location); + + if (!needsFallback) continue; - } + + // Drop through. We need to create a V8 binding in case the V4 binding is invalidated } // Pre-rewrite the expression @@ -3609,16 +3615,23 @@ bool QQmlCompiler::completeComponentBuild() bool isSharable = false; binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable); - if (isSharable && !binding.property->isValueTypeSubProperty && !binding.property->isAlias /* See above re alias */ && - binding.property->type != qMetaTypeId()) { - binding.dataType = BindingReference::V8; + if (isSharable && binding.property->type != qMetaTypeId()) { sharedBindings.append(b); + + if (!needsFallback) { + binding.dataType = BindingReference::V8; + binding.compiledIndex = -1; + + if (componentStats) + componentStats->componentStat.sharedBindings.append(b->value->location); + } } else { + Q_ASSERT(!needsFallback); binding.dataType = BindingReference::QtScript; - } - if (componentStats) - componentStats->componentStat.scriptBindings.append(b->value->location); + if (componentStats) + componentStats->componentStat.scriptBindings.append(b->value->location); + } } if (!sharedBindings.isEmpty()) { @@ -3650,7 +3663,10 @@ bool QQmlCompiler::completeComponentBuild() functionArray += expression.toUtf8(); lineNumber += expression.count(QLatin1Char('\n')); - reference->compiledIndex = ii; + + // Ensure the index value fits within the available space + Q_ASSERT(ii < (1 << 15)); + reference->sharedIndex = ii; } functionArray.append("]", 1); @@ -3689,9 +3705,9 @@ void QQmlCompiler::dumpStats() output.append(" "); } - output.append("("); + output.append('('); output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line)); - output.append(":"); + output.append(':'); output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column)); output.append(") "); } @@ -3699,18 +3715,37 @@ void QQmlCompiler::dumpStats() qWarning().nospace() << output.constData(); } + qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count(); + { + QByteArray output; + for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) { + if (0 == (ii % 10)) { + if (ii) output.append('\n'); + output.append(" "); + } + + output.append('('); + output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line)); + output.append(':'); + output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column)); + output.append(") "); + } + if (!output.isEmpty()) + qWarning().nospace() << output.constData(); + } + qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count(); { QByteArray output; for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) { if (0 == (ii % 10)) { - if (ii) output.append("\n"); + if (ii) output.append('\n'); output.append(" "); } - output.append("("); + output.append('('); output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line)); - output.append(":"); + output.append(':'); output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column)); output.append(") "); } @@ -3726,13 +3761,13 @@ void QQmlCompiler::dumpStats() */ bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from) { - const QMetaObject *toMo = enginePrivate->rawMetaObjectForType(to); - const QMetaObject *fromMo = from->metaObject(); + QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); + QQmlPropertyCache *fromMo = from->metatype; while (fromMo) { - if (QQmlPropertyPrivate::equal(fromMo, toMo)) + if (fromMo == toMo) return true; - fromMo = fromMo->superClass(); + fromMo = fromMo->parent(); } return false; } @@ -3744,7 +3779,7 @@ QString QQmlCompiler::elementName(QQmlScript::Object *o) { Q_ASSERT(o); if (o->type != -1) { - return output->types.at(o->type).className; + return unit->parser().referencedTypes().at(o->type)->name; } else { return QString(); } @@ -3752,8 +3787,10 @@ QString QQmlCompiler::elementName(QQmlScript::Object *o) QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from) { - // ### Optimize - const QMetaObject *mo = from->metatype; + if (from->type != -1 && output->types.at(from->type).type) + return output->types.at(from->type).type; + + const QMetaObject *mo = from->metatype->firstCppMetaObject(); QQmlType *type = 0; while (!type && mo) { type = QQmlMetaType::qmlType(mo); @@ -3764,7 +3801,7 @@ QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from) QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj) { - const QMetaObject *mo = obj->metatype; + const QMetaObject *mo = obj->metatype->firstCppMetaObject(); int idx = mo->indexOfClassInfo("DeferredPropertyNames"); if (idx == -1) @@ -3775,17 +3812,21 @@ QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj) return rv; } +QQmlPropertyCache * +QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object) + { + if (object->synthCache) + return object->synthCache; + else if (object->type != -1) + return output->types[object->type].createPropertyCache(engine); + else + return object->metatype; +} + QQmlPropertyData * QQmlCompiler::property(QQmlScript::Object *object, int index) { - QQmlPropertyCache *cache = 0; - - if (object->synthCache) - cache = object->synthCache; - else if (object->type != -1) - cache = output->types[object->type].createPropertyCache(engine); - else - cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + QQmlPropertyCache *cache = propertyCacheForObject(object); return cache->property(index); } @@ -3795,14 +3836,7 @@ QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, { if (notInRevision) *notInRevision = false; - QQmlPropertyCache *cache = 0; - - if (object->synthCache) - cache = object->synthCache; - else if (object->type != -1) - cache = output->types[object->type].createPropertyCache(engine); - else - cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + QQmlPropertyCache *cache = propertyCacheForObject(object); QQmlPropertyData *d = cache->property(name); @@ -3824,14 +3858,7 @@ QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, b { if (notInRevision) *notInRevision = false; - QQmlPropertyCache *cache = 0; - - if (object->synthCache) - cache = object->synthCache; - else if (object->type != -1) - cache = output->types[object->type].createPropertyCache(engine); - else - cache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + QQmlPropertyCache *cache = propertyCacheForObject(object); QQmlPropertyData *d = cache->property(name); @@ -3843,7 +3870,7 @@ QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, b if (d && !cache->isAllowedInRevision(d)) { if (notInRevision) *notInRevision = true; return 0; - } else if (d) { + } else if (d && d->isSignal()) { return d; } @@ -3852,7 +3879,7 @@ QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, b d = property(object, propName, notInRevision); if (d) - return cache->method(d->notifyIndex); + return cache->signal(d->notifyIndex); } return 0;