#include "qqmlpropertyvaluesource.h"
#include "qqmlcomponent.h"
#include <private/qmetaobjectbuilder_p.h>
-#include <private/qfastmetabuilder_p.h>
#include "qqmlstringconverters_p.h"
#include "qqmlengine_p.h"
#include "qqmlengine.h"
#include "qqmlbinding_p.h"
#include <private/qv4compiler_p.h>
-#include <QColor>
#include <QDebug>
#include <QPointF>
#include <QSizeF>
#include <QAtomicInt>
#include <QtCore/qdebug.h>
#include <QtCore/qdatetime.h>
+#include <QtCore/qvarlengtharray.h>
Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QList<qreal>)
Q_DECLARE_METATYPE(QList<bool>)
Q_DECLARE_METATYPE(QList<QString>)
Q_DECLARE_METATYPE(QList<QUrl>)
+Q_DECLARE_METATYPE(QJSValue)
QT_BEGIN_NAMESPACE
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"));
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())
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;
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()) {
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:
COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected"));
}
break;
+ } else if (type == qMetaTypeId<QJSValue>()) {
+ 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.
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);
output->addInstruction(instr);
}
} else {
- if (prop->core.isVMEProperty()) {
+ if (prop->core.isVarProperty()) {
Instruction::StoreVarDouble instr;
instr.propertyIndex = prop->index;
instr.value = n;
}
}
} 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();
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());
{
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);
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;
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;
} else if (type == qMetaTypeId<QList<QUrl> >()) {
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);
instr.value = output->indexForString(v->value.asString());
output->addInstruction(instr);
break;
+ } else if (type == qMetaTypeId<QJSValue>()) {
+ 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
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;
}
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<QMetaObject &>(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);
}
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;
}
// 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);
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<QChar>(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()) {
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;
}
create.type = obj->type;
create.line = obj->location.start.line;
create.column = obj->location.start.column;
+ create.parentToSuper = parentToSuper;
output->addInstruction(create);
} else {
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;
}
// 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();
Q_ASSERT(propertyCache);
propertyCache->addref();
- // Add flag for alias properties
- if (!obj->synthdata.isEmpty()) {
- const QQmlVMEMetaData *vmeMetaData =
- reinterpret_cast<const QQmlVMEMetaData *>(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;
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);
}
pop.type = prop->type;
pop.bindingSkipList = 0;
output->addInstruction(pop);
+
+ genPropertyAssignment(prop, obj);
}
void QQmlCompiler::genComponent(QQmlScript::Object *obj)
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;
}
}
QQmlCompiledData::TypeReference ref;
- ref.className = Component_string;
ref.type = t;
output->types << ref;
cachedComponentTypeRef = output->types.count() - 1;
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();
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);
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
}
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,
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;
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
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"));
// 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"));
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));
}
} 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;
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);
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"));
+ }
+ }
}
}
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;
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"));
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)) {
//optimization for <Type>.<EnumValue> 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) {
// 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();
}
}
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<Object>();
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<Value>();
componentValue->object = root;
// 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;
// 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;
if (v->value.isScript()) {
//optimization for <Type>.<EnumValue> 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) {
return true;
}
+struct StaticQtMetaObject : public QObject
+{
+ static const QMetaObject *get()
+ { return &static_cast<StaticQtMetaObject*> (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()));
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);
}
} 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);
+ }
}
}
return true;
}
-struct StaticQtMetaObject : public QObject
-{
- static const QMetaObject *get()
- { return &static_cast<StaticQtMetaObject*> (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;
}
return true;
}
-Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter)
+#include <private/qqmljsparser_p.h>
+
+static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
+{
+ if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {
+ QString name =
+ static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
+ return QStringList() << name;
+ } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
+ QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(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)
{
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<QQmlListProperty<QObject> >();
- }
+ 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<QByteArray> names;
+ QVarLengthArray<int, 10> 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<QQmlListProperty<QObject> >();
+ }
- // 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<QQmlListProperty<QObject> >();
+ }
+ }
- // 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 <private/qqmljsparser_p.h>
+ 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<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
- return QStringList() << name;
- } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
- QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(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 <id>, <id>.<property> or <id>.<value property>.<property>"));
- 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 <id>, <id>.<property> or <id>.<value property>.<property>"));
+ 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;
}
{
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()));
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 |
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<JSBindingReference &>(*binding->bindingReference);
+ js.dataType = BindingReference::V8;
+ genBindingAssignment(binding, prop, obj, valueTypeProperty);
+ }
} else if (ref.dataType == BindingReference::V8) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(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;
} else if (ref.dataType == BindingReference::QtScript) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(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");
}
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);
}
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;
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
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<QQmlBinding*>()) {
- binding.dataType = BindingReference::V8;
+ if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
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()) {
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);
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(") ");
}
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(") ");
}
*/
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;
}
{
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();
}
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);
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)
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);
}
{
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);
{
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);
if (d && !cache->isAllowedInRevision(d)) {
if (notInRevision) *notInRevision = true;
return 0;
- } else if (d) {
+ } else if (d && d->isSignal()) {
return d;
}
d = property(object, propName, notInRevision);
if (d)
- return cache->method(d->notifyIndex);
+ return cache->signal(d->notifyIndex);
}
return 0;