1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4serialize_p.h"
41
42#include <private/qv4value_p.h>
43#include <private/qv4dateobject_p.h>
44#include <private/qv4regexpobject_p.h>
45#if QT_CONFIG(qml_sequence_object)
46#include <private/qv4sequenceobject_p.h>
47#endif
48#include <private/qv4objectproto_p.h>
49#include <private/qv4qobjectwrapper_p.h>
50
51QT_BEGIN_NAMESPACE
52
53using namespace QV4;
54
55// We allow the following JavaScript types to be passed between the main and
56// the secondary thread:
57// + undefined
58// + null
59// + Boolean
60// + String
61// + Function
62// + Array
63// + "Simple" Objects
64// + Number
65// + Date
66// + RegExp
67// <quint8 type><quint24 size><data>
68
69enum Type {
70 WorkerUndefined,
71 WorkerNull,
72 WorkerTrue,
73 WorkerFalse,
74 WorkerString,
75 WorkerFunction,
76 WorkerArray,
77 WorkerObject,
78 WorkerInt32,
79 WorkerUint32,
80 WorkerNumber,
81 WorkerDate,
82 WorkerRegexp,
83 WorkerListModel,
84 WorkerUrl,
85#if QT_CONFIG(qml_sequence_object)
86 WorkerSequence
87#endif
88};
89
90static inline quint32 valueheader(Type type, quint32 size = 0)
91{
92 return quint8(type) << 24 | (size & 0xFFFFFF);
93}
94
95static inline Type headertype(quint32 header)
96{
97 return (Type)(header >> 24);
98}
99
100static inline quint32 headersize(quint32 header)
101{
102 return header & 0xFFFFFF;
103}
104
105static inline void push(QByteArray &data, quint32 value)
106{
107 data.append(s: (const char *)&value, len: sizeof(quint32));
108}
109
110static inline void push(QByteArray &data, double value)
111{
112 data.append(s: (const char *)&value, len: sizeof(double));
113}
114
115static inline void push(QByteArray &data, void *ptr)
116{
117 data.append(s: (const char *)&ptr, len: sizeof(void *));
118}
119
120static inline void reserve(QByteArray &data, int extra)
121{
122 data.reserve(asize: data.size() + extra);
123}
124
125static inline quint32 popUint32(const char *&data)
126{
127 quint32 rv = *((const quint32 *)data);
128 data += sizeof(quint32);
129 return rv;
130}
131
132static inline double popDouble(const char *&data)
133{
134 double rv = *((const double *)data);
135 data += sizeof(double);
136 return rv;
137}
138
139static inline void *popPtr(const char *&data)
140{
141 void *rv = *((void *const *)data);
142 data += sizeof(void *);
143 return rv;
144}
145
146#define ALIGN(size) (((size) + 3) & ~3)
147static inline void serializeString(QByteArray &data, const QString &str, Type type)
148{
149 int length = str.length();
150 if (length > 0xFFFFFF) {
151 push(data, value: valueheader(type: WorkerUndefined));
152 return;
153 }
154 int utf16size = ALIGN(length * sizeof(quint16));
155
156 reserve(data, extra: utf16size + sizeof(quint32));
157 push(data, value: valueheader(type, size: length));
158
159 int offset = data.size();
160 data.resize(size: data.size() + utf16size);
161 char *buffer = data.data() + offset;
162
163 memcpy(dest: buffer, src: str.constData(), n: length*sizeof(QChar));
164}
165
166// XXX TODO: Check that worker script is exception safe in the case of
167// serialization/deserialization failures
168
169void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine)
170{
171 QV4::Scope scope(engine);
172
173 if (v.isEmpty()) {
174 Q_ASSERT(!"Serialize: got empty value");
175 } else if (v.isUndefined()) {
176 push(data, value: valueheader(type: WorkerUndefined));
177 } else if (v.isNull()) {
178 push(data, value: valueheader(type: WorkerNull));
179 } else if (v.isBoolean()) {
180 push(data, value: valueheader(type: v.booleanValue() == true ? WorkerTrue : WorkerFalse));
181 } else if (v.isString()) {
182 serializeString(data, str: v.toQString(), type: WorkerString);
183 } else if (v.as<FunctionObject>()) {
184 // XXX TODO: Implement passing function objects between the main and
185 // worker scripts
186 push(data, value: valueheader(type: WorkerUndefined));
187 } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) {
188 uint length = array->getLength();
189 if (length > 0xFFFFFF) {
190 push(data, value: valueheader(type: WorkerUndefined));
191 return;
192 }
193 reserve(data, extra: sizeof(quint32) + length * sizeof(quint32));
194 push(data, value: valueheader(type: WorkerArray, size: length));
195 ScopedValue val(scope);
196 for (uint ii = 0; ii < length; ++ii)
197 serialize(data, v: (val = array->get(idx: ii)), engine);
198 } else if (v.isInteger()) {
199 reserve(data, extra: 2 * sizeof(quint32));
200 push(data, value: valueheader(type: WorkerInt32));
201 push(data, value: (quint32)v.integerValue());
202// } else if (v.IsUint32()) {
203// reserve(data, 2 * sizeof(quint32));
204// push(data, valueheader(WorkerUint32));
205// push(data, v.Uint32Value());
206 } else if (v.isNumber()) {
207 reserve(data, extra: sizeof(quint32) + sizeof(double));
208 push(data, value: valueheader(type: WorkerNumber));
209 push(data, value: v.asDouble());
210 } else if (const QV4::DateObject *d = v.as<DateObject>()) {
211 reserve(data, extra: sizeof(quint32) + sizeof(double));
212 push(data, value: valueheader(type: WorkerDate));
213 push(data, value: d->date());
214 } else if (const RegExpObject *re = v.as<RegExpObject>()) {
215 quint32 flags = re->flags();
216 QString pattern = re->source();
217 int length = pattern.length() + 1;
218 if (length > 0xFFFFFF) {
219 push(data, value: valueheader(type: WorkerUndefined));
220 return;
221 }
222 int utf16size = ALIGN(length * sizeof(quint16));
223
224 reserve(data, extra: sizeof(quint32) + utf16size);
225 push(data, value: valueheader(type: WorkerRegexp, size: flags));
226 push(data, value: (quint32)length);
227
228 int offset = data.size();
229 data.resize(size: data.size() + utf16size);
230 char *buffer = data.data() + offset;
231
232 memcpy(dest: buffer, src: pattern.constData(), n: length*sizeof(QChar));
233 } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
234 // XXX TODO: Generalize passing objects between the main thread and worker scripts so
235 // that others can trivially plug in their elements.
236 if (QObject *lm = qobjectWrapper->object()) {
237 if (QObject *agent = qvariant_cast<QObject *>(v: lm->property(name: "agent"))) {
238 if (QMetaObject::invokeMethod(obj: agent, member: "addref")) {
239 push(data, value: valueheader(type: WorkerListModel));
240 push(data, ptr: (void *)agent);
241 return;
242 }
243 }
244 }
245 // No other QObject's are allowed to be sent
246 push(data, value: valueheader(type: WorkerUndefined));
247 } else if (const Object *o = v.as<Object>()) {
248#if QT_CONFIG(qml_sequence_object)
249 if (o->isListType()) {
250 // valid sequence. we generate a length (sequence length + 1 for the sequence type)
251 uint seqLength = ScopedValue(scope, o->get(name: engine->id_length()))->toUInt32();
252 uint length = seqLength + 1;
253 if (length > 0xFFFFFF) {
254 push(data, value: valueheader(type: WorkerUndefined));
255 return;
256 }
257 reserve(data, extra: sizeof(quint32) + length * sizeof(quint32));
258 push(data, value: valueheader(type: WorkerSequence, size: length));
259 serialize(data, v: QV4::Value::fromInt32(i: QV4::SequencePrototype::metaTypeForSequence(object: o)), engine); // sequence type
260 ScopedValue val(scope);
261 for (uint ii = 0; ii < seqLength; ++ii)
262 serialize(data, v: (val = o->get(idx: ii)), engine); // sequence elements
263
264 return;
265 }
266#endif
267 const QVariant variant = engine->toVariant(value: v, typeHint: QMetaType::QUrl, createJSValueForObjects: false);
268 if (variant.userType() == QMetaType::QUrl) {
269 serializeString(data, str: variant.value<QUrl>().toString(), type: WorkerUrl);
270 return;
271 }
272
273 // regular object
274 QV4::ScopedValue val(scope, v);
275 QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4: engine, o: val));
276 quint32 length = properties->getLength();
277 if (length > 0xFFFFFF) {
278 push(data, value: valueheader(type: WorkerUndefined));
279 return;
280 }
281 push(data, value: valueheader(type: WorkerObject, size: length));
282
283 QV4::ScopedValue s(scope);
284 for (quint32 ii = 0; ii < length; ++ii) {
285 s = properties->get(idx: ii);
286 serialize(data, v: s, engine);
287
288 QV4::String *str = s->as<String>();
289 val = o->get(name: str);
290 if (scope.hasException())
291 scope.engine->catchException();
292
293 serialize(data, v: val, engine);
294 }
295 return;
296 } else {
297 push(data, value: valueheader(type: WorkerUndefined));
298 }
299}
300
301struct VariantRef
302{
303 VariantRef() : obj(nullptr) {}
304 VariantRef(const VariantRef &r) : obj(r.obj) { addref(); }
305 VariantRef(QObject *a) : obj(a) { addref(); }
306 ~VariantRef() { release(); }
307
308 VariantRef &operator=(const VariantRef &o) {
309 o.addref();
310 release();
311 obj = o.obj;
312 return *this;
313 }
314
315 void addref() const
316 {
317 if (obj)
318 QMetaObject::invokeMethod(obj, member: "addref");
319 }
320
321 void release() const
322 {
323 if (obj)
324 QMetaObject::invokeMethod(obj, member: "release");
325
326 }
327
328 QObject *obj;
329};
330
331QT_END_NAMESPACE
332Q_DECLARE_METATYPE(VariantRef)
333Q_DECLARE_METATYPE(QV4::ExecutionEngine *)
334QT_BEGIN_NAMESPACE
335
336ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
337{
338 quint32 header = popUint32(data);
339 Type type = headertype(header);
340
341 Scope scope(engine);
342
343 switch (type) {
344 case WorkerUndefined:
345 return QV4::Encode::undefined();
346 case WorkerNull:
347 return QV4::Encode::null();
348 case WorkerTrue:
349 return QV4::Encode(true);
350 case WorkerFalse:
351 return QV4::Encode(false);
352 case WorkerString:
353 case WorkerUrl:
354 {
355 quint32 size = headersize(header);
356 QString qstr((const QChar *)data, size);
357 data += ALIGN(size * sizeof(quint16));
358 return (type == WorkerUrl)
359 ? engine->fromVariant(QVariant::fromValue(value: QUrl(qstr)))
360 : Encode(engine->newString(s: qstr));
361 }
362 case WorkerFunction:
363 Q_ASSERT(!"Unreachable");
364 break;
365 case WorkerArray:
366 {
367 quint32 size = headersize(header);
368 ScopedArrayObject a(scope, engine->newArrayObject());
369 ScopedValue v(scope);
370 for (quint32 ii = 0; ii < size; ++ii) {
371 v = deserialize(data, engine);
372 a->put(idx: ii, v);
373 }
374 return a.asReturnedValue();
375 }
376 case WorkerObject:
377 {
378 quint32 size = headersize(header);
379 ScopedObject o(scope, engine->newObject());
380 ScopedValue name(scope);
381 ScopedString n(scope);
382 ScopedValue value(scope);
383 for (quint32 ii = 0; ii < size; ++ii) {
384 name = deserialize(data, engine);
385 value = deserialize(data, engine);
386 n = name->asReturnedValue();
387 o->put(name: n, v: value);
388 }
389 return o.asReturnedValue();
390 }
391 case WorkerInt32:
392 return QV4::Encode((qint32)popUint32(data));
393 case WorkerUint32:
394 return QV4::Encode(popUint32(data));
395 case WorkerNumber:
396 return QV4::Encode(popDouble(data));
397 case WorkerDate:
398 return QV4::Encode(engine->newDateObject(value: QV4::Value::fromDouble(d: popDouble(data))));
399 case WorkerRegexp:
400 {
401 quint32 flags = headersize(header);
402 quint32 length = popUint32(data);
403 QString pattern = QString((const QChar *)data, length - 1);
404 data += ALIGN(length * sizeof(quint16));
405 return Encode(engine->newRegExpObject(pattern, flags));
406 }
407 case WorkerListModel:
408 {
409 QObject *agent = reinterpret_cast<QObject *>(popPtr(data));
410 QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, object: agent));
411 // ### Find a better solution then the ugly property
412 VariantRef ref(agent);
413 QVariant var = QVariant::fromValue(value: ref);
414 QV4::ScopedValue v(scope, scope.engine->fromVariant(var));
415 QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref")));
416 rv->as<Object>()->defineReadonlyProperty(name: s, value: v);
417
418 QMetaObject::invokeMethod(obj: agent, member: "release");
419 agent->setProperty(name: "engine", value: QVariant::fromValue(value: engine));
420 return rv->asReturnedValue();
421 }
422#if QT_CONFIG(qml_sequence_object)
423 case WorkerSequence:
424 {
425 ScopedValue value(scope);
426 bool succeeded = false;
427 quint32 length = headersize(header);
428 quint32 seqLength = length - 1;
429 value = deserialize(data, engine);
430 int sequenceType = value->integerValue();
431 ScopedArrayObject array(scope, engine->newArrayObject());
432 array->arrayReserve(n: seqLength);
433 for (quint32 ii = 0; ii < seqLength; ++ii) {
434 value = deserialize(data, engine);
435 array->arrayPut(index: ii, value);
436 }
437 array->setArrayLengthUnchecked(seqLength);
438 QVariant seqVariant = QV4::SequencePrototype::toVariant(array, typeHint: sequenceType, succeeded: &succeeded);
439 return QV4::SequencePrototype::fromVariant(engine, v: seqVariant, succeeded: &succeeded);
440 }
441#endif
442 }
443 Q_ASSERT(!"Unreachable");
444 return QV4::Encode::undefined();
445}
446
447QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine)
448{
449 QByteArray rv;
450 serialize(data&: rv, v: value, engine);
451 return rv;
452}
453
454ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine)
455{
456 const char *stream = data.constData();
457 return deserialize(data&: stream, engine);
458}
459
460QT_END_NAMESPACE
461

source code of qtdeclarative/src/qmlworkerscript/qv4serialize.cpp