QQmlData *data = QQmlData::get(obj, true);
- if (index & 0xFF000000) {
+ if (index & 0xFFFF0000) {
// Value type
- int coreIndex = index & 0xFFFFFF;
+ int coreIndex = index & 0x0000FFFF;
// Find the value type proxy (if there is one)
QQmlValueTypeProxyBinding *proxy = 0;
QQmlData *data = QQmlData::get(obj, false);
Q_ASSERT(data);
- if (index & 0xFF000000) {
+ if (index & 0xFFFF0000) {
// Find the value type binding
QQmlAbstractBinding *vtbinding = data->bindings;
- while (vtbinding->propertyIndex() != (index & 0xFFFFFF)) {
+ while (vtbinding->propertyIndex() != (index & 0x0000FFFF)) {
vtbinding = vtbinding->nextBinding();
Q_ASSERT(vtbinding);
}
// Should return the encoded property index for the binding. Should return this value
// even if the binding is not enabled or added to an object.
- // Encoding is: coreIndex | (valueTypeIndex << 24)
+ // Encoding is: coreIndex | (valueTypeIndex << 16)
int propertyIndex() const { return vtable()->propertyIndex(this); }
+
// Should return the object for the binding. Should return this object even if the
// binding is not enabled or added to the object.
QObject *object() const { return vtable()->object(this); }
Q_ASSERT(prop->index != -1);
if (QQmlValueTypeFactory::isValueType(prop->type)) {
- if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) {
-
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type);
+ if (prop->type >= 0 && valueType) {
if (!prop->values.isEmpty()) {
// 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)) {
}
}
- COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type],
- prop->value, obj, ctxt.incr()));
+ COMPILE_CHECK(buildValueTypeProperty(valueType, 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
}
obj->addValueTypeProperty(prop);
-
} else {
COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
}
-
} else {
// Load the nested property's meta type
prop->value->metatype = enginePrivate->propertyCacheForType(prop->type);
COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
int propIdx = -1;
+ int propType = 0;
int notifySignal = -1;
int flags = 0;
int type = 0;
if (alias.count() == 2 || alias.count() == 3) {
QQmlPropertyData *property = this->property(idObject, alias.at(1));
- if (!property || property->coreIndex > 0xFFFF)
+ if (!property || property->coreIndex > 0x0000FFFF)
COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
propIdx = property->coreIndex;
notifySignal = property->notifyIndex;
if (alias.count() == 3) {
- QQmlValueType *valueType = enginePrivate->valueTypes[type]; // XXX threadsafe?
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
if (!valueType)
COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
- propIdx |= ((unsigned int)type) << 24;
+ propType = type;
+
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);
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
propIdx |= (valueTypeIndex << 16);
if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
propertyFlags |= QQmlPropertyData::IsQObjectDerived;
}
- QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags, notifySignal };
+ QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal };
typedef QQmlVMEMetaData VMD;
VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
store.owner = js.bindingContext.owner;
store.isAlias = prop->isAlias;
if (valueTypeProperty) {
- store.property = (valueTypeProperty->index & 0xFFFF) |
- ((valueTypeProperty->type & 0xFF)) << 16 |
- ((prop->index & 0xFF) << 24);
+ store.property = ((prop->index << 16) | valueTypeProperty->index);
+ store.propType = valueTypeProperty->type;
store.isRoot = (compileState->root == valueTypeProperty->parent);
} else {
store.property = prop->index;
+ store.propType = 0;
store.isRoot = (compileState->root == obj);
}
store.line = binding->location.start.line;
QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
QQmlScript::Property *prop)
{
- typedef QQmlPropertyPrivate QDPP;
- return QDPP::saveValueType(prop->core, enginePrivate->valueTypes[prop->type]->metaObject(),
- valueTypeProp->index, engine);
+ QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type);
+ Q_ASSERT(vt);
+ return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine);
}
bool QQmlCompiler::completeComponentBuild()
return uniqueId++;
}
- QQmlValueTypeFactory valueTypes;
-
// Unfortunate workaround to avoid a circular dependency between
// qqmlengine_p.h and qqmlincubator_p.h
struct Incubator {
};
struct instr_assignV4Binding {
QML_INSTR_HEADER
- unsigned int property;
+ int property; // ((value type sub-property index << 16) | property index)
+ int propType;
int value;
int fallbackValue;
short context;
if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; }
}
-Q_GLOBAL_STATIC(QQmlValueTypeFactory, qmlValueTypes);
-
QQmlPropertyPrivate::QQmlPropertyPrivate()
: context(0), engine(0), object(0), isNameCached(false)
{
return; // Not an object property
if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) {
- // We're now at a value type property. We can use a global valuetypes array as we
- // never actually use the objects, just look up their properties.
- QObject *typeObject = (*qmlValueTypes())[property->propType];
+ // We're now at a value type property
+ QObject *typeObject = QQmlValueTypeFactory::valueType(property->propType);
if (!typeObject) return; // Not a value type
int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData());
QMetaProperty vtProp = typeObject->metaObject()->property(idx);
- typedef QQmlPropertyData PCD;
-
- Q_ASSERT(PCD::flagsForProperty(vtProp) <= PCD::ValueTypeFlagMask);
- Q_ASSERT(vtProp.userType() <= 0xFF);
- Q_ASSERT(idx <= 0xFF);
+ Q_ASSERT(QQmlPropertyData::flagsForProperty(vtProp) <= QQmlPropertyData::ValueTypeFlagMask);
+ Q_ASSERT(vtProp.userType() <= 0x0000FFFF);
+ Q_ASSERT(idx <= 0x0000FFFF);
object = currentObject;
core = *property;
- core.setFlags(core.getFlags() | PCD::IsValueTypeVirtual);
- core.valueTypeFlags = PCD::flagsForProperty(vtProp);
+ core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
+ core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp);
core.valueTypePropType = vtProp.userType();
core.valueTypeCoreIndex = idx;
return QQmlProperty::List;
else
return QQmlProperty::Normal;
- } else {
- return QQmlProperty::InvalidCategory;
}
+
+ return QQmlProperty::InvalidCategory;
}
/*!
if (!d)
return 0;
if (d->isValueType()) {
-
- QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0;
- QQmlValueType *valueType = 0;
- if (ep) valueType = ep->valueTypes[d->core.propType];
- else valueType = QQmlValueTypeFactory::valueType(d->core.propType);
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType);
Q_ASSERT(valueType);
-
- const char *rv = valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName();
-
- if (!ep) delete valueType;
-
- return rv;
+ return valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName();
} else if (d->object && type() & Property && d->core.isValid()) {
return d->object->metaObject()->property(d->core.coreIndex).typeName();
} else {
} else if (d->isValueType()) {
QString rv = d->core.name(d->object) + QLatin1Char('.');
- QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0;
- QQmlValueType *valueType = 0;
- if (ep) valueType = ep->valueTypes[d->core.propType];
- else valueType = QQmlValueTypeFactory::valueType(d->core.propType);
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType);
Q_ASSERT(valueType);
const char *vtName = valueType->metaObject()->property(d->core.valueTypeCoreIndex).name();
rv += QString::fromUtf8(vtName);
- if (!ep) delete valueType;
-
d->nameCache = rv;
} else if (type() & SignalProperty) {
QString name = QLatin1String("on") + d->core.name(d->object);
QObject *object = newBinding->object();
int pi = newBinding->propertyIndex();
- int core = pi & 0xFFFFFF;
- int vt = (pi & 0xFF000000)?(pi >> 24):-1;
+ int core = pi & 0x0000FFFF;
+ int vt = (pi & 0xFFFF0000)?(pi >> 16):-1;
return setBinding(object, core, vt, newBinding, flags);
} else {
if (binding && valueTypeIndex != -1) {
if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) {
- int index = coreIndex | (valueTypeIndex << 24);
+ int index = coreIndex | (valueTypeIndex << 16);
binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
}
}
void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex,
QObject **targetObject, int *targetBindingIndex)
{
- int coreIndex = bindingIndex & 0xFFFFFF;
- int valueTypeIndex = bindingIndex >> 24;
- if (valueTypeIndex == 0) valueTypeIndex = -1;
+ int coreIndex = bindingIndex & 0x0000FFFF;
+ int valueTypeIndex = (bindingIndex & 0xFFFF0000)?(bindingIndex >> 16):-1;
QQmlData *data = QQmlData::get(object, false);
if (data) {
int aBindingIndex = aCoreIndex;
if (aValueTypeIndex != -1)
- aBindingIndex |= aValueTypeIndex << 24;
+ aBindingIndex |= aValueTypeIndex << 16;
else if (valueTypeIndex != -1)
- aBindingIndex |= valueTypeIndex << 24;
+ aBindingIndex |= valueTypeIndex << 16;
findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
return;
int index = coreIndex;
if (valueTypeIndex != -1)
- index |= (valueTypeIndex << 24);
+ index |= (valueTypeIndex << 16);
if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy)
binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
int index = coreIndex;
if (valueTypeIndex != -1)
- index |= (valueTypeIndex << 24);
+ index |= (valueTypeIndex << 16);
if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy)
binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
QObject *object = newBinding->object();
int pi = newBinding->propertyIndex();
- int core = pi & 0xFFFFFF;
- int vt = (pi & 0xFF000000)?(pi >> 24):-1;
+ int core = pi & 0x0000FFFF;
+ int vt = (pi & 0xFFFF0000)?(pi >> 16):-1;
return setBinding(object, core, vt, newBinding, flags);
}
{
if (isValueType()) {
- QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
- QQmlValueType *valueType = 0;
- if (ep) valueType = ep->valueTypes[core.propType];
- else valueType = QQmlValueTypeFactory::valueType(core.propType);
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType);
Q_ASSERT(valueType);
-
valueType->read(object, core.coreIndex);
-
- QVariant rv = valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType);
-
- if (!ep) delete valueType;
- return rv;
+ return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType);
} else if (core.isQList()) {
bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags)
{
- return writeValueProperty(object, engine, core, value, effectiveContext(), flags);
+ return writeValueProperty(object, core, value, effectiveContext(), flags);
}
bool
-QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlEngine *engine,
- const QQmlPropertyData &core,
- const QVariant &value,
- QQmlContextData *context, WriteFlags flags)
+QQmlPropertyPrivate::writeValueProperty(QObject *object,
+ const QQmlPropertyData &core,
+ const QVariant &value,
+ QQmlContextData *context, WriteFlags flags)
{
// Remove any existing bindings on this property
if (!(flags & DontRemoveBinding) && object) {
bool rv = false;
if (core.isValueTypeVirtual()) {
- QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
-
- QQmlValueType *writeBack = 0;
- if (ep) {
- writeBack = ep->valueTypes[core.propType];
- } else {
- writeBack = QQmlValueTypeFactory::valueType(core.propType);
- }
+ QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType);
writeBack->read(object, core.coreIndex);
QQmlPropertyData data = core;
rv = write(writeBack, data, value, context, flags);
writeBack->write(object, core.coreIndex, flags);
- if (!ep) delete writeBack;
} else {
void *args[] = { 0 };
QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args);
} else if (isUndefined && type == qMetaTypeId<QVariant>()) {
- writeValueProperty(object, engine, core, QVariant(), context, flags);
+ writeValueProperty(object, core, QVariant(), context, flags);
} else if (type == qMetaTypeId<QJSValue>()) {
if (!result.IsEmpty() && result->IsFunction()
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
return false;
}
- writeValueProperty(object, engine, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
+ writeValueProperty(object, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
} else if (isUndefined) {
QString errorStr = QLatin1String("Unable to assign [undefined] to ");
if (!QMetaType::typeName(type))
else
expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
return false;
- } else if (!writeValueProperty(object, engine, core, value, context, flags)) {
+ } else if (!writeValueProperty(object, core, value, context, flags)) {
if (watcher.wasDeleted())
return true;
}
/*!
- Returns the "property index" for use in bindings. The top 8 bits are the value type
- offset, and 0 otherwise. The bottom 24-bits are the regular property index.
+ Returns the "property index" for use in bindings. The top 16 bits are the value type
+ offset, and 0 otherwise. The bottom 16 bits are the regular property index.
*/
int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that)
{
{
int rv = that.coreIndex;
if (rv != -1 && that.isValueTypeVirtual())
- rv = rv | (that.valueTypeCoreIndex << 24);
+ rv = rv | (that.valueTypeCoreIndex << 16);
return rv;
}
static QQmlMetaObject rawMetaObjectForType(QQmlEnginePrivate *, int);
static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object,
const QVariant &value, int flags);
- static bool writeValueProperty(QObject *, QQmlEngine *,
+ static bool writeValueProperty(QObject *,
const QQmlPropertyData &,
const QVariant &, QQmlContextData *,
WriteFlags flags = 0);
inline int getValueTypeCoreIndex() const;
// Returns the "encoded" index for use with bindings. Encoding is:
- // coreIndex | (valueTypeCoreIndex << 24)
+ // coreIndex | (valueTypeCoreIndex << 16)
inline int encodedIndex() const;
union {
struct { // When IsValueTypeVirtual
quint16 valueTypeFlags; // flags of the access property on the value type proxy
// object
- quint8 valueTypePropType; // The QVariant::Type of access property on the value
- // type proxy object
- quint8 valueTypeCoreIndex; // The prop index of the access property on the value
+ quint16 valueTypePropType; // The QVariant::Type of access property on the value
// type proxy object
+ quint16 valueTypeCoreIndex; // The prop index of the access property on the value
+ // type proxy object
};
struct { // When !IsValueTypeVirtual
int QQmlPropertyRawData::encodedIndex() const
{
- return isValueTypeVirtual()?(coreIndex | (valueTypeCoreIndex << 24)):coreIndex;
+ return isValueTypeVirtual()?(coreIndex | (valueTypeCoreIndex << 16)):coreIndex;
}
QQmlPropertyData *
QT_BEGIN_NAMESPACE
-QQmlValueTypeFactory::QQmlValueTypeFactory()
+namespace {
+
+struct QQmlValueTypeFactoryImpl
+{
+ QQmlValueTypeFactoryImpl();
+ ~QQmlValueTypeFactoryImpl();
+
+ bool isValueType(int idx);
+
+ QQmlValueType *createValueType(int);
+ QQmlValueType *valueType(int);
+
+ QQmlValueType *valueTypes[QVariant::UserType];
+ QHash<int, QQmlValueType *> userTypes;
+ QMutex mutex;
+};
+
+QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
{
for (unsigned int ii = 0; ii < QVariant::UserType; ++ii)
valueTypes[ii] = 0;
}
-QQmlValueTypeFactory::~QQmlValueTypeFactory()
+QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl()
{
- for (unsigned int ii = 0; ii < QVariant::UserType; ++ii)
- delete valueTypes[ii];
+ qDeleteAll(valueTypes, valueTypes + QVariant::UserType);
+ qDeleteAll(userTypes);
}
-bool QQmlValueTypeFactory::isValueType(int idx)
+bool QQmlValueTypeFactoryImpl::isValueType(int idx)
{
- if ((uint)idx < QVariant::UserType
+ if (idx >= QVariant::UserType) {
+ return (valueType(idx) != 0);
+ } else if (idx >= 0
&& idx != QVariant::StringList
&& idx != QMetaType::QObjectStar
&& idx != QMetaType::QWidgetStar
&& idx != QMetaType::QVariant) {
return true;
}
- return false;
-}
-void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, int versionMinor)
-{
- qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing");
+ return false;
}
-QQmlValueType *QQmlValueTypeFactory::valueType(int t)
+QQmlValueType *QQmlValueTypeFactoryImpl::createValueType(int t)
{
QQmlValueType *rv = 0;
return rv;
}
+QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx)
+{
+ if (idx >= (int)QVariant::UserType) {
+ // Protect the hash with a mutex
+ mutex.lock();
+
+ QHash<int, QQmlValueType *>::iterator it = userTypes.find(idx);
+ if (it == userTypes.end()) {
+ it = userTypes.insert(idx, createValueType(idx));
+ }
+
+ mutex.unlock();
+ return *it;
+ }
+
+ QQmlValueType *rv = valueTypes[idx];
+ if (!rv) {
+ // No need for mutex protection - the most we can lose is a valueType instance
+
+ // TODO: Investigate the performance/memory characteristics of
+ // removing the preallocated array
+ if ((rv = createValueType(idx))) {
+ valueTypes[idx] = rv;
+ }
+ }
+
+ return rv;
+}
+
+}
+
+Q_GLOBAL_STATIC(QQmlValueTypeFactoryImpl, factoryImpl);
+
+bool QQmlValueTypeFactory::isValueType(int idx)
+{
+ return factoryImpl()->isValueType(idx);
+}
+
+QQmlValueType *QQmlValueTypeFactory::valueType(int idx)
+{
+ return factoryImpl()->valueType(idx);
+}
+
+void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, int versionMinor)
+{
+ qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing");
+}
+
+
QQmlValueType::QQmlValueType(int userType, QObject *parent)
: QObject(parent), m_userType(userType)
{
}
-
QQmlPointFValueType::QQmlPointFValueType(QObject *parent)
: QQmlValueTypeBase<QPointF>(QMetaType::QPointF, parent)
{
class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory
{
public:
- QQmlValueTypeFactory();
- ~QQmlValueTypeFactory();
static bool isValueType(int);
- static QQmlValueType *valueType(int);
+ static QQmlValueType *valueType(int idx);
static void registerValueTypes(const char *uri, int versionMajor, int versionMinor);
-
- QQmlValueType *operator[](int idx) const {
- if (idx >= (int)QVariant::UserType)
- return 0;
-
- QQmlValueType *rv = valueTypes[idx];
- if (!rv) {
- // Table update is not thread-safe, but the potential for leaks is
- // so small that the cost of protection is unwarranted
- if ((rv = valueType(idx))) {
- valueTypes[idx] = rv;
- }
- }
- return rv;
- }
-
-private:
- mutable QQmlValueType *valueTypes[QVariant::UserType];
};
class Q_QML_PRIVATE_EXPORT QQmlPointFValueType : public QQmlValueTypeBase<QPointF>
QQmlAbstractBinding *lastBinding = 0;
while (binding) {
- if (mask & (1 << (binding->propertyIndex() >> 24))) {
+ if (mask & (1 << (binding->propertyIndex() >> 16))) {
QQmlAbstractBinding *remove = binding;
binding = remove->nextBinding();
{
QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(o)->declarativeData);
- index &= 0xFFFFFF; // To handle value types
+ index &= 0x0000FFFF; // To handle value types
return ddata && (ddata->bindingBitsSize > index) &&
(ddata->bindingBits[index / 32] & (1 << (index % 32)));
static void removeBindingOnProperty(QObject *o, int index)
{
- int coreIndex = index & 0xFFFFFF;
- int valueTypeIndex = index & 0xFF000000;
- if (!valueTypeIndex) valueTypeIndex = -1;
+ int coreIndex = index & 0x0000FFFF;
+ int valueTypeIndex = (index & 0xFFFF0000 ? index >> 16 : -1);
QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0);
if (binding) binding->destroy();
QObject *scope =
objects.at(objects.count() - 1 - instr.context);
- int property = instr.property;
- if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF))
+ int propertyIdx = (instr.property & 0x0000FFFF);
+
+ if (instr.isRoot && BINDINGSKIPLIST.testBit(propertyIdx))
QML_NEXT_INSTR(StoreV4Binding);
QQmlAbstractBinding *binding =
- CTXT->v4bindings->configBinding(instr.value, instr.fallbackValue, target, scope, property,
- instr.line, instr.column);
+ CTXT->v4bindings->configBinding(instr.value, instr.fallbackValue, target, scope, instr.property,
+ instr.propType, instr.line, instr.column);
bindValues.push(binding);
binding->m_mePtr = &bindValues.top();
if (instr.isAlias) {
- int valueTypeIndex = (property & 0x00FF0000) ? (property >> 24) : -1;
QQmlAbstractBinding *old =
QQmlPropertyPrivate::setBindingNoEnable(target,
- property & 0xFFFF,
- valueTypeIndex,
+ propertyIdx,
+ instr.propType ? (instr.property >> 16) : -1,
binding);
if (old) { old->destroy(); }
} else {
- Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF));
+ Q_ASSERT(binding->propertyIndex() == instr.property);
Q_ASSERT(binding->object() == target);
- CLEAN_PROPERTY(target, property & 0xFF00FFFF);
+ CLEAN_PROPERTY(target, instr.property);
binding->addToObject();
}
}
}
- QQmlValueType *valueHandler = ep->valueTypes[instr.type];
+ QQmlValueType *valueHandler = QQmlValueTypeFactory::valueType(instr.type);
+ Q_ASSERT(valueHandler);
valueHandler->read(target, instr.property);
objects.push(valueHandler);
QML_END_INSTR(FetchValueType)
if (type != QVariant::Invalid) {
if (valueIndex != -1) {
- QQmlEnginePrivate *ep = ctxt?QQmlEnginePrivate::get(ctxt->engine):0;
- QQmlValueType *valueType = 0;
- if (ep) valueType = ep->valueTypes[type];
- else valueType = QQmlValueTypeFactory::valueType(type);
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
Q_ASSERT(valueType);
//
updated = true;
}
- if (!ep)
- delete valueType;
-
if (updated)
return -1;
} else {
if (d->isValueTypeAlias()) {
// Value type property
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine);
-
- QQmlValueType *valueType = ep->valueTypes[d->valueType()];
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType());
Q_ASSERT(valueType);
valueType->read(target, d->propertyIndex());
struct AliasData {
int contextIdx;
int propertyIdx;
+ int propType;
int flags;
int notifySignal;
return propertyIdx == -1;
}
bool isPropertyAlias() const {
- return !isObjectAlias() && !(propertyIdx & 0xFF000000);
+ return !isObjectAlias() && !(propertyIdx & 0xFFFF0000);
}
bool isValueTypeAlias() const {
- return !isObjectAlias() && (propertyIdx & 0xFF000000);
+ return !isObjectAlias() && (propertyIdx & 0xFFFF0000);
}
int propertyIndex() const {
return propertyIdx & 0x0000FFFF;
}
int valueTypeIndex() const {
- return (propertyIdx & 0x00FF0000) >> 16;
+ return (propertyIdx & 0xFFFF0000) >> 16;
}
int valueType() const {
- return ((unsigned int)propertyIdx) >> 24;
+ return (propertyIdx & 0xFFFF0000) ? propType : 0;
}
};
delete [] subscriptions; subscriptions = 0;
}
-QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target,
- QObject *scope, int property,
- int line, int column)
+QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target, QObject *scope,
+ int property, int propType, int line, int column)
{
+ Q_ASSERT(propType <= std::numeric_limits<quint16>::max());
+
Binding *rv = bindings + index;
rv->index = index;
rv->fallbackIndex = fallbackIndex;
rv->property = property;
+ rv->propType = propType;
rv->target = target;
rv->scope = scope;
rv->line = line;
const QV4Bindings::Binding *This = static_cast<const QV4Bindings::Binding *>(_This);
if (This->target.hasValue()) return This->target.constValue()->targetProperty;
- //mask out the type information set for value types
- else return This->property & 0xFF00FFFF;
+ else return This->property;
}
QObject *QV4Bindings::Binding::object(const QQmlAbstractBinding *_This)
if (binding->updating) {
QString name;
- if (binding->property & 0xFFFF0000) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
-
- QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF];
+ if (binding->propType) {
+ QQmlValueType *vt = QQmlValueTypeFactory::valueType(binding->propType);
Q_ASSERT(vt);
- name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name());
+ name = QLatin1String(binding->target->metaObject()->property(binding->property & 0x0000FFFF).name());
name.append(QLatin1Char('.'));
- name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name()));
+ name.append(QLatin1String(vt->metaObject()->property(binding->property >> 16).name()));
} else {
name = QLatin1String(binding->target->metaObject()->property(binding->property).name());
}
bool *inv = (binding->fallbackIndex != -1) ? &invalidated : 0;
binding->updating = true;
- if (binding->property & 0xFFFF0000) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
-
- QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF];
+ if (binding->propType) {
+ QQmlValueType *vt = QQmlValueTypeFactory::valueType(binding->propType);
Q_ASSERT(vt);
- vt->read(*binding->target, binding->property & 0xFFFF);
+ vt->read(*binding->target, binding->property & 0x0000FFFF);
QObject *target = vt;
run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv);
if (!invalidated) {
- vt->write(*binding->target, binding->property & 0xFFFF, flags);
+ vt->write(*binding->target, binding->property & 0x0000FFFF, flags);
}
} else {
QQmlData *data = QQmlData::get(*binding->target);
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
QV8Engine *v8engine = ep->v8engine();
- QQmlValueType *vt = ep->valueTypes[QMetaType::QColor];
+ QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor);
v8::HandleScope handle_scope;
v8::Context::Scope scope(v8engine->context());
new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal(
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- QQmlValueType *vt = ep->valueTypes[QMetaType::QColor];
+ QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor);
new (output.gethandleptr()) v8::Handle<v8::Value>(ep->v8engine()->valueTypeWrapper()->newValueType(tmp, vt));
V8HANDLE_REGISTER(instr->unaryop.output);
}
virtual ~QV4Bindings();
QQmlAbstractBinding *configBinding(int index, int fallbackIndex, QObject *target,
- QObject *scope, int property,
- int line, int column);
+ QObject *scope, int property, int propType,
+ int line, int column);
#ifdef QML_THREADED_INTERPRETER
static void **getDecodeInstrTable();
struct Binding : public QQmlAbstractBinding, public QQmlDelayedError {
Binding() : QQmlAbstractBinding(V4), index(-1), fallbackIndex(-1), enabled(false),
- updating(0), property(0), scope(0), target(0), executedBlocks(0), parent(0) {}
+ updating(0), property(0), propType(0), scope(0), target(0), executedBlocks(0), parent(0) {}
// Inherited from QQmlAbstractBinding
static void destroy(QQmlAbstractBinding *);
bool enabled:1;
bool updating:1;
- // Encoding of property is coreIndex | (propType << 16) | (valueTypeIndex << 24)
+ // Encoding of property is: coreIndex | (valueTypeIndex << 16)
// propType and valueTypeIndex are only set if the property is a value type property
int property;
+ quint16 propType;
+
QObject *scope;
int line;
int column;
break;
}
- if (m_engine) {
- if (QQmlValueType *vt = QQmlEnginePrivate::get(m_engine)->valueTypes[type])
- return m_valueTypeWrapper.newValueType(variant, vt);
- }
-
+ if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
+ return m_valueTypeWrapper.newValueType(variant, vt);
} else {
if (type == qMetaTypeId<QQmlListReference>()) {
typedef QQmlListReferencePrivate QDLRP;
v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
if (succeeded)
return retn;
+
+ if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
+ return m_valueTypeWrapper.newValueType(variant, vt);
}
// XXX TODO: To be compatible, we still need to handle:
} else if (property.isQVariant()) {
QVariant v;
ReadFunction(object, property, &v, notifier);
- if (QQmlValueTypeFactory::isValueType(v.userType()) && engine->engine()) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
- QQmlValueType *valueType = ep->valueTypes[v.userType()];
- if (valueType)
+
+ if (QQmlValueTypeFactory::isValueType(v.userType())) {
+ if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType()))
return engine->newValueType(object, property.coreIndex, valueType); // VariantReference value-type.
}
+
return engine->fromVariant(v);
- } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
- && engine->engine()) {
+ } else if (QQmlValueTypeFactory::isValueType(property.propType)) {
Q_ASSERT(notifier == 0);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
- QQmlValueType *valueType = ep->valueTypes[property.propType];
- if (valueType)
+ if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType))
return engine->newValueType(object, property.coreIndex, valueType);
} else {
Q_ASSERT(notifier == 0);
// overwritten with a different type in the meantime.
// We need to modify this reference to the updated value type, if
// possible, or return false if it is not a value type.
- QQmlEngine *e = reference->engine->engine();
- if (QQmlValueTypeFactory::isValueType(variantReferenceType) && e) {
- reference->type = QQmlEnginePrivate::get(e)->valueTypes[variantReferenceType];
+ if (QQmlValueTypeFactory::isValueType(variantReferenceType)) {
+ reference->type = QQmlValueTypeFactory::valueType(variantReferenceType);
if (!reference->type) {
return false;
}
--- /dev/null
+import QtQuick 2.0
+import Test 1.0
+
+Item {
+ property bool success: false
+
+ // Test user value type stored as both var and variant
+ property var testValue1
+ property variant testValue2
+ property variant testValue3
+ property var testValue4
+
+ TestValueExporter {
+ id: assignmentValueType
+ testValue.property1: 1
+ testValue.property2: 3.1415927
+ }
+
+ TestValueExporter {
+ id: v4BindingValueType
+ testValue.property1: 1 + 2
+ testValue.property2: 3.1415927 / 2.0
+ }
+
+ TestValueExporter {
+ id: v8BindingValueType
+ testValue.property1: if (true) 1 + 2
+ testValue.property2: if (true) 3.1415927 / 2.0
+ }
+
+ function numberEqual(lhs, rhs) {
+ var d = (lhs - rhs)
+ return (Math.abs(d) < 0.0001)
+ }
+
+ Component.onCompleted: {
+ // Poperties assigned the result of Q_INVOKABLE:
+ testValue1 = testValueExporter.getTestValue()
+ testValue2 = testValueExporter.getTestValue()
+
+ if (testValue1.property1 != 333) return
+ if (!numberEqual(testValue1.property2, 666.999)) return
+
+ if (testValue2.property1 != 333) return
+ if (!numberEqual(testValue2.property2, 666.999)) return
+
+ if (testValue1 != testValue2) return
+
+ // Write to the properties of the value type
+ testValue1.property1 = 1
+ testValue1.property2 = 3.1415927
+
+ testValue2.property1 = 1
+ testValue2.property2 = 3.1415927
+
+ if (testValue1.property1 != 1) return
+ if (!numberEqual(testValue1.property2, 3.1415927)) return
+
+ if (testValue2.property1 != 1) return
+ if (!numberEqual(testValue2.property2, 3.1415927)) return
+
+ if (testValue1 != testValue2) return
+
+ // Assignment of value type properties
+ testValue3 = testValue1
+ testValue4 = testValue2
+
+ if (testValue3.property1 != 1) return
+ if (!numberEqual(testValue3.property2, 3.1415927)) return
+
+ if (testValue4.property1 != 1) return
+ if (!numberEqual(testValue4.property2, 3.1415927)) return
+
+ if (testValue3 != testValue4) return
+
+ // Access a value-type property of a QObject
+ var vt = testValueExporter.testValue
+ if (vt.property1 != 0) return
+ if (!numberEqual(vt.property2, 0.0)) return
+
+ testValueExporter.testValue = testValue4
+
+ if (vt.property1 != 1) return
+ if (!numberEqual(vt.property2, 3.1415927)) return
+
+ success = true
+ }
+}
#include <qtest.h>
#include <QQmlEngine>
#include <QQmlComponent>
+#include <QQmlContext>
#include <QDebug>
+#include <private/qqmlglobal_p.h>
#include <private/qquickvaluetypes_p.h>
#include "../../shared/util.h"
#include "testtypes.h"
void cppIntegration();
void jsObjectConversion();
void invokableFunctions();
+ void userType();
};
void tst_qqmlvaluetypeproviders::initTestCase()
delete object;
}
+namespace {
+
+// A value-type class to export to QML
+class TestValue
+{
+public:
+ TestValue() : m_p1(0), m_p2(0.0) {}
+ TestValue(int p1, double p2) : m_p1(p1), m_p2(p2) {}
+ TestValue(const TestValue &other) : m_p1(other.m_p1), m_p2(other.m_p2) {}
+ ~TestValue() {}
+
+ TestValue &operator=(const TestValue &other) { m_p1 = other.m_p1; m_p2 = other.m_p2; return *this; }
+
+ int property1() const { return m_p1; }
+ void setProperty1(int p1) { m_p1 = p1; }
+
+ double property2() const { return m_p2; }
+ void setProperty2(double p2) { m_p2 = p2; }
+
+ bool operator==(const TestValue &other) const { return (m_p1 == other.m_p1) && (m_p2 == other.m_p2); }
+ bool operator!=(const TestValue &other) const { return !operator==(other); }
+
+private:
+ int m_p1;
+ double m_p2;
+};
+
+}
+
+Q_DECLARE_METATYPE(TestValue);
+
+namespace {
+
+class TestValueType : public QQmlValueTypeBase<TestValue>
+{
+ Q_OBJECT
+ Q_PROPERTY(int property1 READ property1 WRITE setProperty1)
+ Q_PROPERTY(double property2 READ property2 WRITE setProperty2)
+public:
+ TestValueType(QObject *parent = 0) : QQmlValueTypeBase<TestValue>(qMetaTypeId<TestValue>(), parent) {}
+
+ virtual QString toString() const { return QString::number(property1()) + QLatin1Char(',') + QString::number(property2()); }
+ virtual bool isEqual(const QVariant &other) const { return (other.userType() == qMetaTypeId<TestValue>()) && (v == other.value<TestValue>()); }
+
+ int property1() const { return v.property1(); }
+ void setProperty1(int p1) { v.setProperty1(p1); }
+
+ double property2() const { return v.property2(); }
+ void setProperty2(double p2) { v.setProperty2(p2); }
+};
+
+class TestValueTypeProvider : public QQmlValueTypeProvider
+{
+public:
+ bool create(int type, QQmlValueType *&v)
+ {
+ if (type == qMetaTypeId<TestValue>()) {
+ v = new TestValueType;
+ return true;
+ }
+
+ return false;
+ }
+
+};
+
+TestValueTypeProvider *getValueTypeProvider()
+{
+ static TestValueTypeProvider valueTypeProvider;
+ return &valueTypeProvider;
+}
+
+bool initializeProviders()
+{
+ QQml_addValueTypeProvider(getValueTypeProvider());
+ return true;
+}
+
+const bool initialized = initializeProviders();
+
+class TestValueExporter : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(TestValue testValue READ testValue WRITE setTestValue)
+public:
+ TestValue testValue() const { return m_testValue; }
+ void setTestValue(const TestValue &v) { m_testValue = v; }
+
+ Q_INVOKABLE TestValue getTestValue() const { return TestValue(333, 666.999); }
+
+private:
+ TestValue m_testValue;
+};
+
+}
+
+void tst_qqmlvaluetypeproviders::userType()
+{
+ Q_ASSERT(initialized);
+ Q_ASSERT(qMetaTypeId<TestValue>() >= QMetaType::User);
+
+ qRegisterMetaType<TestValue>();
+ qmlRegisterType<TestValueExporter>("Test", 1, 0, "TestValueExporter");
+
+ TestValueExporter exporter;
+
+ QQmlEngine e;
+ e.rootContext()->setContextProperty("testValueExporter", &exporter);
+
+ QQmlComponent component(&e, testFileUrl("userType.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj != 0);
+ QCOMPARE(obj->property("success").toBool(), true);
+}
+
QTEST_MAIN(tst_qqmlvaluetypeproviders)
#include "tst_qqmlvaluetypeproviders.moc"