Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmllistaccessor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <private/qqmlmetatype_p.h>
8
9#include <QtCore/qdebug.h>
10#include <QtCore/qsequentialiterable.h>
11#include <QtCore/qstringlist.h>
12#include <QtCore/qurl.h>
13
15
16QQmlListAccessor::QQmlListAccessor()
17: m_type(Invalid)
18{
19}
20
21QQmlListAccessor::~QQmlListAccessor()
22{
23}
24
25QVariant QQmlListAccessor::list() const
26{
27 return d;
28}
29
30void QQmlListAccessor::setList(const QVariant &v)
31{
32 d = v;
33
34 // An incoming JS array as model is treated as a variant list, so we need to
35 // convert it first with toVariant().
36 QMetaType variantsType = d.metaType();
37 if (variantsType == QMetaType::fromType<QJSValue>()) {
38 d = d.value<QJSValue>().toVariant();
39 variantsType = d.metaType();
40 }
41
42 if (!d.isValid()) {
43 m_type = Invalid;
44 return;
45 }
46
47 if (variantsType == QMetaType::fromType<QStringList>()) {
48 m_type = StringList;
49 return;
50 }
51
52 if (variantsType == QMetaType::fromType<QList<QUrl>>()) {
53 m_type = UrlList;
54 return;
55 }
56
57 if (variantsType == QMetaType::fromType<QVariantList>()) {
58 m_type = VariantList;
59 return;
60 }
61
62 if (variantsType == QMetaType::fromType<QList<QObject *>>()) {
63 m_type = ObjectList;
64 return;
65 }
66
67 if (variantsType.flags() & QMetaType::IsQmlList) {
68 d = QVariant::fromValue(QQmlListReference(d));
69 m_type = ListProperty;
70 return;
71 }
72
73 if (variantsType == QMetaType::fromType<QQmlListReference>()) {
74 m_type = ListProperty;
75 return;
76 }
77
78 if (variantsType.flags() & QMetaType::PointerToQObject) {
79 m_type = Instance;
80 return;
81 }
82
83 if (int i = 0; [&](){bool ok = false; i = v.toInt(&ok); return ok;}()) {
84 // Here we have to check for an upper limit, because down the line code might (well, will)
85 // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX:
86 // QVector<QPointer<QQuickItem>> something;
87 // something.resize(count());
88 // (See e.g. QQuickRepeater::regenerate())
89 // This will allocate data along the lines of:
90 // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize
91 // So, doing an approximate round-down-to-nice-number, we get:
92 const int upperLimit = 100 * 1000 * 1000;
93
94 if (i < 0) {
95 qWarning("Model size of %d is less than 0", i);
96 m_type = Invalid;
97 return;
98 }
99
100 if (i > upperLimit) {
101 qWarning("Model size of %d is bigger than the upper limit %d", i, upperLimit);
102 m_type = Invalid;
103 return;
104 }
105
106 m_type = Integer;
107 d = i;
108 return;
109 }
110
111 const QQmlType type = QQmlMetaType::qmlListType(variantsType);
112 if (type.isSequentialContainer()) {
113 m_metaSequence = type.listMetaSequence();
114 m_type = m_metaSequence.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)
115 ? ObjectSequence
116 : ValueSequence;
117 return;
118 }
119
120 QSequentialIterable iterable;
121 if (QMetaType::convert(
122 variantsType, d.constData(),
123 QMetaType::fromType<QSequentialIterable>(), &iterable)) {
124 const QMetaSequence sequence = iterable.metaContainer();
125
126 if (sequence.hasSize() && sequence.canGetValueAtIndex()) {
127 // If the resulting iterable is useful for anything, use it.
128 m_metaSequence = sequence;
129 m_type = sequence.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)
130 ? ObjectSequence
131 : ValueSequence;
132 return;
133 }
134
135 if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
136 // As a last resort, try to read the contents of the container via an iterator
137 // and build a QVariantList from them.
138 QVariantList variantList;
139 for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
140 variantList.push_back(*it);
141 d = std::move(variantList);
142 m_type = VariantList;
143 return;
144 }
145 }
146
147 m_type = Instance;
148 return;
149}
150
151qsizetype QQmlListAccessor::count() const
152{
153 switch(m_type) {
154 case StringList:
155 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
156 return reinterpret_cast<const QStringList *>(d.constData())->size();
157 case UrlList:
158 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
159 return reinterpret_cast<const QList<QUrl> *>(d.constData())->size();
160 case VariantList:
161 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
162 return reinterpret_cast<const QVariantList *>(d.constData())->size();
163 case ObjectList:
164 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
165 return reinterpret_cast<const QList<QObject *> *>(d.constData())->size();
166 case ListProperty:
167 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
168 return reinterpret_cast<const QQmlListReference *>(d.constData())->count();
169 case ValueSequence:
170 case ObjectSequence:
171 Q_ASSERT(m_metaSequence != QMetaSequence());
172 return m_metaSequence.size(d.constData());
173 case Instance:
174 return 1;
175 case Integer:
176 return *reinterpret_cast<const int *>(d.constData());
177 case Invalid:
178 return 0;
179 }
180 Q_UNREACHABLE_RETURN(0);
181}
182
183QVariant QQmlListAccessor::at(qsizetype idx) const
184{
185 Q_ASSERT(idx >= 0 && idx < count());
186 switch(m_type) {
187 case StringList:
188 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
189 return QVariant::fromValue(reinterpret_cast<const QStringList *>(d.constData())->at(idx));
190 case UrlList:
191 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
192 return QVariant::fromValue(reinterpret_cast<const QList<QUrl> *>(d.constData())->at(idx));
193 case VariantList:
194 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
195 return reinterpret_cast<const QVariantList *>(d.constData())->at(idx);
196 case ObjectList:
197 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
198 return QVariant::fromValue(reinterpret_cast<const QList<QObject *> *>(d.constData())->at(idx));
199 case ListProperty:
200 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
201 return QVariant::fromValue(reinterpret_cast<const QQmlListReference *>(d.constData())->at(idx));
202 case ValueSequence:
203 case ObjectSequence: {
204 Q_ASSERT(m_metaSequence != QMetaSequence());
205 QVariant result;
206 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
207 if (valueMetaType == QMetaType::fromType<QVariant>()) {
208 m_metaSequence.valueAtIndex(d.constData(), idx, &result);
209 } else {
210 result = QVariant(valueMetaType);
211 m_metaSequence.valueAtIndex(d.constData(), idx, result.data());
212 }
213 return result;
214 }
215 case Instance:
216 return d;
217 case Integer:
218 return QVariant(idx);
219 case Invalid:
220 return QVariant();
221 }
222 Q_UNREACHABLE_RETURN(QVariant());
223}
224
225void QQmlListAccessor::set(qsizetype idx, const QVariant &value)
226{
227 Q_ASSERT(idx >= 0 && idx < count());
228 switch (m_type) {
229 case StringList:
230 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
231 (*static_cast<QStringList *>(d.data()))[idx] = value.toString();
232 break;
233 case UrlList:
234 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
235 (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>();
236 break;
237 case VariantList:
238 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
239 (*static_cast<QVariantList *>(d.data()))[idx] = value;
240 break;
241 case ObjectList:
242 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
243 (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>();
244 break;
245 case ListProperty:
246 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
247 static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>());
248 break;
249 case ValueSequence:
250 case ObjectSequence: {
251 Q_ASSERT(m_metaSequence != QMetaSequence());
252 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
253 if (valueMetaType == QMetaType::fromType<QVariant>()) {
254 m_metaSequence.setValueAtIndex(d.data(), idx, &value);
255 } else if (valueMetaType == value.metaType()) {
256 m_metaSequence.setValueAtIndex(d.data(), idx, value.constData());
257 } else {
258 QVariant converted = value;
259 converted.convert(valueMetaType);
260 m_metaSequence.setValueAtIndex(d.data(), idx, converted.constData());
261 }
262 break;
263 }
264 case Instance:
265 d = value;
266 break;
267 case Integer:
268 break;;
269 case Invalid:
270 break;
271 }
272}
273
274bool QQmlListAccessor::isValid() const
275{
276 return m_type != Invalid;
277}
278
279QT_END_NAMESPACE