diff options
Diffstat (limited to 'src/qml/types/qqmllistmodel.cpp')
-rw-r--r-- | src/qml/types/qqmllistmodel.cpp | 211 |
1 files changed, 156 insertions, 55 deletions
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 830c2bef5a..1b64a38bd6 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -62,6 +62,8 @@ #include <QtCore/qdatetime.h> #include <QScopedValueRollback> +Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); + QT_BEGIN_NAMESPACE // Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. @@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) { - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; Role *r = new Role; r->name = key; @@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV data.value<QJSValue>().isCallable()) { type = Role::Function; break; + } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() + && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { + type = Role::String; + break; } else { type = Role::List; break; @@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const return r; } +StringOrTranslation::StringOrTranslation(const QString &s) +{ + d.setFlag(); + setString(s); +} + +StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +StringOrTranslation::~StringOrTranslation() +{ + clear(); +} + +void StringOrTranslation::setString(const QString &s) +{ + d.setFlag(); + clear(); + QStringData *stringData = const_cast<QString &>(s).data_ptr(); + d = stringData; + if (stringData) + stringData->ref.ref(); +} + +void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +QString StringOrTranslation::toString(const QQmlListModel *owner) const +{ + if (d.isNull()) + return QString(); + if (d.isT1()) { + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); + } + if (!owner) + return QString(); + return d.asT2()->valueAsString(owner->m_compilationUnit.data()); +} + +QString StringOrTranslation::asString() const +{ + if (d.isNull()) + return QString(); + if (!d.isT1()) + return QString(); + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); +} + +void StringOrTranslation::clear() +{ + if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { + if (!strData->ref.deref()) + QStringData::deallocate(strData); + } + d = static_cast<QStringData *>(nullptr); +} + QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) { ListElement *e = elements[elementIndex]; @@ -512,7 +587,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -593,7 +668,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache() return ModelNodeMetaObject::get(m_objectCache); } -QString *ListElement::getStringProperty(const ListLayout::Role &role) +StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) { char *mem = getPropertyMemory(role); - QString *s = reinterpret_cast<QString *>(mem); - return s->data_ptr() ? s : nullptr; + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + return s; } QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) @@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo break; case ListLayout::Role::String: { - QString *value = reinterpret_cast<QString *>(mem); - if (value->data_ptr() != nullptr) - data = *value; + StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); + if (value->isSet()) + data = value->toString(owner); } break; case ListLayout::Role::Bool: @@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString & if (role.type == ListLayout::Role::String) { char *mem = getPropertyMemory(role); - QString *c = reinterpret_cast<QString *>(mem); + StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); bool changed; - if (c->data_ptr() == nullptr) { - new (mem) QString(s); + if (!c->isSet() || c->isTranslation()) changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } + else + changed = c->asString().compare(s) != 0; + c->setString(s); if (changed) roleIndex = role.index; } @@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu return roleIndex; } +int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + s->setTranslation(b); + roleIndex = role.index; + } + + return roleIndex; +} + void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) { char *mem = getPropertyMemory(role); - new (mem) QString(s); + new (mem) StringOrTranslation(s); } void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) @@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout) switch (r.type) { case ListLayout::Role::String: { - QString *string = getStringProperty(r); + StringOrTranslation *string = getStringProperty(r); if (string) - string->~QString(); + string->~StringOrTranslation(); } break; case ListLayout::Role::List: @@ -1284,7 +1371,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant roleIndex = setDoubleProperty(role, d.toDouble()); break; case ListLayout::Role::String: - roleIndex = setStringProperty(role, d.toString()); + if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) + roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); + else + roleIndex = setStringProperty(role, d.toString()); break; case ListLayout::Role::Bool: roleIndex = setBoolProperty(role, d.toBool()); @@ -1332,7 +1422,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d ListModel *subModel = new ListModel(role.subLayout, nullptr); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } roleIndex = setListProperty(role, subModel); @@ -1446,7 +1536,7 @@ void ModelNodeMetaObject::propertyWritten(int index) return; QString propName = QString::fromUtf8(name(index)); - QVariant value = operator[](index); + const QVariant value = this->value(index); QV4::Scope scope(m_model->engine()); QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); @@ -1474,29 +1564,37 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo namespace QV4 { -bool ModelObject::put(Managed *m, String *name, const Value &value) +bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + QString propName = id.toQString(); + ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *eng = that->engine(); const int elementIndex = that->d()->elementIndex(); - const QString propName = name->toQString(); int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); if (roleIndex != -1) that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) - mo->emitPropertyNotification(name->toQString().toUtf8()); + mo->emitPropertyNotification(propName.toUtf8()); return true; } -ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isString()) + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); + const ModelObject *that = static_cast<const ModelObject*>(m); + Scope scope(that); + ScopedString name(scope, id.asStringOrSymbol()); const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); if (!role) - return QObjectWrapper::get(m, name, hasProperty); + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); if (hasProperty) *hasProperty = true; @@ -1512,7 +1610,7 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty return that->engine()->fromVariant(value); } -void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *v4 = that->engine(); @@ -1532,7 +1630,7 @@ void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, u // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add // unnecessary entries that relate to the roles used. These just create extra work // later on as they will just be ignored. - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } DEFINE_OBJECT_VTABLE(ModelObject); @@ -1809,6 +1907,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E m_listModel = data; m_engine = engine; + m_compilationUnit = owner->m_compilationUnit; } QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) @@ -1828,6 +1927,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen ListModel::sync(orig->m_listModel, m_listModel); m_engine = nullptr; + m_compilationUnit = orig->m_compilationUnit; } QQmlListModel::~QQmlListModel() @@ -2244,7 +2344,7 @@ void QQmlListModel::insert(QQmlV4Function *args) int objectArrayLength = objectArray->getLength(); emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2356,7 +2456,7 @@ void QQmlListModel::append(QQmlV4Function *args) emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2434,7 +2534,7 @@ QQmlV4Handle QQmlListModel::get(int index) const QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); QQmlData *ddata = QQmlData::get(object); if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); + result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); // Keep track of the QObjectWrapper in persistent value storage ddata->jsWrapper.set(scope.engine, result); } else { @@ -2551,12 +2651,12 @@ void QQmlListModel::sync() qmlWarning(this) << "List sync() can only be called from a WorkerScript"; } -bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding) +bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) { if (binding->type >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex); - QString objName = qmlUnit->stringAt(target->inheritedTypeNameIndex); + const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); + QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); if (objName != listElementTypeName) { const QMetaObject *mo = resolveType(objName); if (mo != &QQmlListElement::staticMetaObject) { @@ -2566,23 +2666,23 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, listElementTypeName = objName; // cache right name for next time } - if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) { + if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) { error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); return false; } const QV4::CompiledData::Binding *binding = target->bindingTable(); for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + QString propName = compilationUnit->stringAt(binding->propertyNameIndex); if (propName.isEmpty()) { error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements")); return false; } - if (!verifyProperty(qmlUnit, binding)) + if (!verifyProperty(compilationUnit, binding)) return false; } } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(qmlUnit); + QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { QByteArray script = scriptStr.toUtf8(); bool ok; @@ -2597,14 +2697,14 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, return true; } -bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) +bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) { - const QString elementName = qmlUnit->stringAt(binding->propertyNameIndex); + const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); bool roleSet = false; if (binding->type >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex); + const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); ListModel *subModel = nullptr; if (outterElementIndex == -1) { @@ -2625,20 +2725,22 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp const QV4::CompiledData::Binding *subBinding = target->bindingTable(); for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(compilationUnit, qmlUnit, subBinding, subModel, elementIndex); + roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex); } } else { QVariant value; - if (binding->evaluatesToString()) { - value = binding->valueAsString(qmlUnit); + if (binding->isTranslationBinding()) { + value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); + } else if (binding->evaluatesToString()) { + value = binding->valueAsString(compilationUnit.data()); } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - value = binding->valueAsNumber(); + value = binding->valueAsNumber(compilationUnit->constants); } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = binding->valueAsBoolean(); } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(qmlUnit); + QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); if (definesEmptyList(scriptStr)) { const ListLayout::Role &role = model->getOrCreateListRole(elementName); ListModel *emptyModel = new ListModel(role.subLayout, nullptr); @@ -2673,35 +2775,34 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp return roleSet; } -void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { listElementTypeName = QString(); // unknown for (const QV4::CompiledData::Binding *binding : bindings) { - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + QString propName = compilationUnit->stringAt(binding->propertyNameIndex); if (!propName.isEmpty()) { // isn't default property error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName)); return; } - if (!verifyProperty(qmlUnit, binding)) + if (!verifyProperty(compilationUnit, binding)) return; } } -void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); rv->m_engine = qmlEngine(rv)->handle(); - - const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; + rv->m_compilationUnit = compilationUnit; bool setRoles = false; for (const QV4::CompiledData::Binding *binding : bindings) { if (binding->type != QV4::CompiledData::Binding::Type_Object) continue; - setRoles |= applyProperty(compilationUnit, qmlUnit, binding, rv->m_listModel, /*outter element index*/-1); + setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); } if (setRoles == false) |