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
qv4serialize.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:critical reason:data-parser
4
6
7#include <private/qv4dateobject_p.h>
8#include <private/qv4objectproto_p.h>
9#include <private/qv4qobjectwrapper_p.h>
10#include <private/qv4regexp_p.h>
11#include <private/qv4regexpobject_p.h>
12#include <private/qv4sequenceobject_p.h>
13#include <private/qv4value_p.h>
14
16
17using namespace QV4;
18
19// We allow the following JavaScript types to be passed between the main and
20// the secondary thread:
21// + undefined
22// + null
23// + Boolean
24// + String
25// + Function
26// + Array
27// + "Simple" Objects
28// + Number
29// + Date
30// + RegExp
31// <quint8 type><quint24 size><data>
32
51
52static inline quint32 valueheader(Type type, quint32 size = 0)
53{
54 return quint8(type) << 24 | (size & 0xFFFFFF);
55}
56
57static inline Type headertype(quint32 header)
58{
59 return (Type)(header >> 24);
60}
61
62static inline quint32 headersize(quint32 header)
63{
64 return header & 0xFFFFFF;
65}
66
67static inline void push(QByteArray &data, quint32 value)
68{
69 data.append((const char *)&value, sizeof(quint32));
70}
71
72static inline void push(QByteArray &data, double value)
73{
74 data.append((const char *)&value, sizeof(double));
75}
76
77static inline void push(QByteArray &data, void *ptr)
78{
79 data.append((const char *)&ptr, sizeof(void *));
80}
81
82static inline void reserve(QByteArray &data, int extra)
83{
84 data.reserve(data.size() + extra);
85}
86
87static inline quint32 popUint32(const char *&data)
88{
89 quint32 rv = *((const quint32 *)data);
90 data += sizeof(quint32);
91 return rv;
92}
93
94static inline double popDouble(const char *&data)
95{
96 double rv = *((const double *)data);
97 data += sizeof(double);
98 return rv;
99}
100
101static inline void *popPtr(const char *&data)
102{
103 void *rv = *((void *const *)data);
104 data += sizeof(void *);
105 return rv;
106}
107
108#define ALIGN(size) (((size) + 3) & ~3)
109static inline void serializeString(QByteArray &data, const QString &str, Type type)
110{
111 int length = str.size();
112 if (length > 0xFFFFFF) {
113 push(data, valueheader(WorkerUndefined));
114 return;
115 }
116 int utf16size = ALIGN(length * sizeof(quint16));
117
118 reserve(data, utf16size + sizeof(quint32));
119 push(data, valueheader(type, length));
120
121 int offset = data.size();
122 data.resize(data.size() + utf16size);
123 char *buffer = data.data() + offset;
124
125 memcpy(buffer, str.constData(), length*sizeof(QChar));
126}
127
128// XXX TODO: Check that worker script is exception safe in the case of
129// serialization/deserialization failures
130
131void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine)
132{
133 QV4::Scope scope(engine);
134
135 if (v.isEmpty()) {
136 Q_ASSERT(!"Serialize: got empty value");
137 } else if (v.isUndefined()) {
138 push(data, valueheader(WorkerUndefined));
139 } else if (v.isNull()) {
140 push(data, valueheader(WorkerNull));
141 } else if (v.isBoolean()) {
142 push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse));
143 } else if (v.isString()) {
144 serializeString(data, v.toQString(), WorkerString);
145 } else if (v.as<FunctionObject>()) {
146 // XXX TODO: Implement passing function objects between the main and
147 // worker scripts
148 push(data, valueheader(WorkerUndefined));
149 } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) {
150 uint length = array->getLength();
151 if (length > 0xFFFFFF) {
152 push(data, valueheader(WorkerUndefined));
153 return;
154 }
155 reserve(data, sizeof(quint32) + length * sizeof(quint32));
156 push(data, valueheader(WorkerArray, length));
157 ScopedValue val(scope);
158 for (uint ii = 0; ii < length; ++ii)
159 serialize(data, (val = array->get(ii)), engine);
160 } else if (v.isInteger()) {
161 reserve(data, 2 * sizeof(quint32));
162 push(data, valueheader(WorkerInt32));
163 push(data, (quint32)v.integerValue());
164// } else if (v.IsUint32()) {
165// reserve(data, 2 * sizeof(quint32));
166// push(data, valueheader(WorkerUint32));
167// push(data, v.Uint32Value());
168 } else if (v.isNumber()) {
169 reserve(data, sizeof(quint32) + sizeof(double));
170 push(data, valueheader(WorkerNumber));
171 push(data, v.asDouble());
172 } else if (const QV4::DateObject *d = v.as<DateObject>()) {
173 reserve(data, sizeof(quint32) + sizeof(double));
174 push(data, valueheader(WorkerDate));
175 push(data, d->date());
176 } else if (const RegExpObject *re = v.as<RegExpObject>()) {
177 quint32 flags = re->flags();
178 QString pattern = re->source();
179 int length = pattern.size() + 1;
180 if (length > 0xFFFFFF) {
181 push(data, valueheader(WorkerUndefined));
182 return;
183 }
184 int utf16size = ALIGN(length * sizeof(quint16));
185
186 reserve(data, sizeof(quint32) + utf16size);
187 push(data, valueheader(WorkerRegexp, flags));
188 push(data, (quint32)length);
189
190 int offset = data.size();
191 data.resize(data.size() + utf16size);
192 char *buffer = data.data() + offset;
193
194 memcpy(buffer, pattern.constData(), length*sizeof(QChar));
195 } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
196 // XXX TODO: Generalize passing objects between the main thread and worker scripts so
197 // that others can trivially plug in their elements.
198 if (QObject *lm = qobjectWrapper->object()) {
199 if (QObject *agent = qvariant_cast<QObject *>(lm->property("agent"))) {
200 if (QMetaObject::invokeMethod(agent, "addref")) {
201 push(data, valueheader(WorkerListModel));
202 push(data, (void *)agent);
203 return;
204 }
205 }
206 }
207 // No other QObject's are allowed to be sent
208 push(data, valueheader(WorkerUndefined));
209 } else if (const Sequence *s = v.as<Sequence>()) {
210 // valid sequence. we generate a length (sequence length + 1 for the sequence type)
211 uint seqLength = ScopedValue(scope, s->get(engine->id_length()))->toUInt32();
212 uint length = seqLength + 1;
213 if (length > 0xFFFFFF) {
214 push(data, valueheader(WorkerUndefined));
215 return;
216 }
217 reserve(data, sizeof(quint32) + length * sizeof(quint32));
218 push(data, valueheader(WorkerSequence, length));
219
220 // sequence type
221 serialize(data, QV4::Value::fromInt32(
222 QV4::SequencePrototype::metaTypeForSequence(s).id()), engine);
223
224 ScopedValue val(scope);
225 for (uint ii = 0; ii < seqLength; ++ii)
226 serialize(data, (val = s->get(ii)), engine); // sequence elements
227
228 return;
229 } else if (const Object *o = v.as<Object>()) {
230 const QVariant variant = QV4::ExecutionEngine::toVariant(
231 v, QMetaType::fromType<QUrl>(), false);
232 if (variant.userType() == QMetaType::QUrl) {
233 serializeString(data, variant.value<QUrl>().toString(), WorkerUrl);
234 return;
235 }
236
237 // regular object
238 QV4::ScopedValue val(scope, v);
239 QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val));
240 quint32 length = properties->getLength();
241 if (length > 0xFFFFFF) {
242 push(data, valueheader(WorkerUndefined));
243 return;
244 }
245 push(data, valueheader(WorkerObject, length));
246
247 QV4::ScopedValue s(scope);
248 for (quint32 ii = 0; ii < length; ++ii) {
249 s = properties->get(ii);
250 serialize(data, s, engine);
251
252 QV4::String *str = s->as<String>();
253 val = o->get(str);
254 if (scope.hasException())
255 scope.engine->catchException();
256
257 serialize(data, val, engine);
258 }
259 return;
260 } else {
261 push(data, valueheader(WorkerUndefined));
262 }
263}
264
266{
267 VariantRef() : obj(nullptr) {}
269 VariantRef(QObject *a) : obj(a) { addref(); }
271
273 o.addref();
274 release();
275 obj = o.obj;
276 return *this;
277 }
278
279 void addref() const
280 {
281 if (obj)
282 QMetaObject::invokeMethod(obj, "addref");
283 }
284
285 void release() const
286 {
287 if (obj)
288 QMetaObject::invokeMethod(obj, "release");
289
290 }
291
293};
294
295QT_END_NAMESPACE
296Q_DECLARE_METATYPE(VariantRef)
297Q_DECLARE_METATYPE(QV4::ExecutionEngine *)
298QT_BEGIN_NAMESPACE
299
300ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
301{
302 quint32 header = popUint32(data);
303 Type type = headertype(header);
304
305 Scope scope(engine);
306
307 switch (type) {
308 case WorkerUndefined:
309 return QV4::Encode::undefined();
310 case WorkerNull:
311 return QV4::Encode::null();
312 case WorkerTrue:
313 return QV4::Encode(true);
314 case WorkerFalse:
315 return QV4::Encode(false);
316 case WorkerString:
317 case WorkerUrl:
318 {
319 quint32 size = headersize(header);
320 QString qstr((const QChar *)data, size);
321 data += ALIGN(size * sizeof(quint16));
322 return (type == WorkerUrl)
323 ? engine->fromVariant(QVariant::fromValue(QUrl(qstr)))
324 : Encode(engine->newString(qstr));
325 }
326 case WorkerFunction:
327 Q_ASSERT(!"Unreachable");
328 break;
329 case WorkerArray:
330 {
331 quint32 size = headersize(header);
332 ScopedArrayObject a(scope, engine->newArrayObject());
333 ScopedValue v(scope);
334 for (quint32 ii = 0; ii < size; ++ii) {
335 v = deserialize(data, engine);
336 a->put(ii, v);
337 }
338 return a.asReturnedValue();
339 }
340 case WorkerObject:
341 {
342 quint32 size = headersize(header);
343 ScopedObject o(scope, engine->newObject());
344 ScopedValue name(scope);
345 ScopedString n(scope);
346 ScopedValue value(scope);
347 for (quint32 ii = 0; ii < size; ++ii) {
348 name = deserialize(data, engine);
349 value = deserialize(data, engine);
350 n = name->asReturnedValue();
351 o->put(n, value);
352 }
353 return o.asReturnedValue();
354 }
355 case WorkerInt32:
356 return QV4::Encode((qint32)popUint32(data));
357 case WorkerUint32:
358 return QV4::Encode(popUint32(data));
359 case WorkerNumber:
360 return QV4::Encode(popDouble(data));
361 case WorkerDate:
362 return QV4::Encode(engine->newDateObject(popDouble(data)));
363 case WorkerRegexp:
364 {
365 quint32 flags = headersize(header);
366 quint32 length = popUint32(data);
367 QString pattern = QString((const QChar *)data, length - 1);
368 data += ALIGN(length * sizeof(quint16));
369 return Encode(engine->newRegExpObject(pattern, flags));
370 }
371 case WorkerListModel:
372 {
373 QObject *agent = reinterpret_cast<QObject *>(popPtr(data));
374 QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent));
375 // ### Find a better solution then the ugly property
376 VariantRef ref(agent);
377 QVariant var = QVariant::fromValue(ref);
378 QV4::ScopedValue v(scope, scope.engine->fromVariant(var));
379 QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref")));
380 rv->as<Object>()->defineReadonlyProperty(s, v);
381
382 QMetaObject::invokeMethod(agent, "release");
383 agent->setProperty("engine", QVariant::fromValue(engine));
384 return rv->asReturnedValue();
385 }
386 case WorkerSequence:
387 {
388 ScopedValue value(scope);
389 quint32 length = headersize(header);
390 quint32 seqLength = length - 1;
391 value = deserialize(data, engine);
392 int sequenceType = value->integerValue();
393 ScopedArrayObject array(scope, engine->newArrayObject());
394 array->arrayReserve(seqLength);
395 for (quint32 ii = 0; ii < seqLength; ++ii) {
396 value = deserialize(data, engine);
397 array->arrayPut(ii, value);
398 }
399 array->setArrayLengthUnchecked(seqLength);
400 QVariant seqVariant = QV4::SequencePrototype::toVariant(array, QMetaType(sequenceType));
401 return QV4::SequencePrototype::fromVariant(engine, seqVariant);
402 }
403 }
404 Q_ASSERT(!"Unreachable");
405 return QV4::Encode::undefined();
406}
407
408QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine)
409{
410 QByteArray rv;
411 serialize(rv, value, engine);
412 return rv;
413}
414
415ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine)
416{
417 const char *stream = data.constData();
418 return deserialize(stream, engine);
419}
420
421QT_END_NAMESPACE
Type
\value X11 \value Windows \value MacPrinter \value CoreGraphics \macos's Quartz2D (CoreGraphics) \val...
#define ALIGN(size, type)
static quint32 valueheader(Type type, quint32 size=0)
static quint32 popUint32(const char *&data)
static void reserve(QByteArray &data, int extra)
@ WorkerNumber
@ WorkerDate
@ WorkerArray
@ WorkerNull
@ WorkerSequence
@ WorkerRegexp
@ WorkerFalse
@ WorkerTrue
@ WorkerFunction
@ WorkerListModel
@ WorkerString
@ WorkerUrl
@ WorkerObject
@ WorkerUint32
@ WorkerInt32
@ WorkerUndefined
static void * popPtr(const char *&data)
static Type headertype(quint32 header)
static double popDouble(const char *&data)
static void push(QByteArray &data, quint32 value)
static quint32 headersize(quint32 header)
static void push(QByteArray &data, void *ptr)
static void serializeString(QByteArray &data, const QString &str, Type type)
static void push(QByteArray &data, double value)
void release() const
VariantRef(const VariantRef &r)
QObject * obj
VariantRef(QObject *a)
VariantRef & operator=(const VariantRef &o)
void addref() const