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
51 QT_BEGIN_NAMESPACE
52
53 using 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
69 enum 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
valueheader(Type type,quint32 size=0)90 static inline quint32 valueheader(Type type, quint32 size = 0)
91 {
92 return quint8(type) << 24 | (size & 0xFFFFFF);
93 }
94
headertype(quint32 header)95 static inline Type headertype(quint32 header)
96 {
97 return (Type)(header >> 24);
98 }
99
headersize(quint32 header)100 static inline quint32 headersize(quint32 header)
101 {
102 return header & 0xFFFFFF;
103 }
104
push(QByteArray & data,quint32 value)105 static inline void push(QByteArray &data, quint32 value)
106 {
107 data.append((const char *)&value, sizeof(quint32));
108 }
109
push(QByteArray & data,double value)110 static inline void push(QByteArray &data, double value)
111 {
112 data.append((const char *)&value, sizeof(double));
113 }
114
push(QByteArray & data,void * ptr)115 static inline void push(QByteArray &data, void *ptr)
116 {
117 data.append((const char *)&ptr, sizeof(void *));
118 }
119
reserve(QByteArray & data,int extra)120 static inline void reserve(QByteArray &data, int extra)
121 {
122 data.reserve(data.size() + extra);
123 }
124
popUint32(const char * & data)125 static inline quint32 popUint32(const char *&data)
126 {
127 quint32 rv = *((const quint32 *)data);
128 data += sizeof(quint32);
129 return rv;
130 }
131
popDouble(const char * & data)132 static inline double popDouble(const char *&data)
133 {
134 double rv = *((const double *)data);
135 data += sizeof(double);
136 return rv;
137 }
138
popPtr(const char * & data)139 static 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)
serializeString(QByteArray & data,const QString & str,Type type)147 static inline void serializeString(QByteArray &data, const QString &str, Type type)
148 {
149 int length = str.length();
150 if (length > 0xFFFFFF) {
151 push(data, valueheader(WorkerUndefined));
152 return;
153 }
154 int utf16size = ALIGN(length * sizeof(quint16));
155
156 reserve(data, utf16size + sizeof(quint32));
157 push(data, valueheader(type, length));
158
159 int offset = data.size();
160 data.resize(data.size() + utf16size);
161 char *buffer = data.data() + offset;
162
163 memcpy(buffer, str.constData(), length*sizeof(QChar));
164 }
165
166 // XXX TODO: Check that worker script is exception safe in the case of
167 // serialization/deserialization failures
168
serialize(QByteArray & data,const QV4::Value & v,ExecutionEngine * engine)169 void 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, valueheader(WorkerUndefined));
177 } else if (v.isNull()) {
178 push(data, valueheader(WorkerNull));
179 } else if (v.isBoolean()) {
180 push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse));
181 } else if (v.isString()) {
182 serializeString(data, v.toQString(), WorkerString);
183 } else if (v.as<FunctionObject>()) {
184 // XXX TODO: Implement passing function objects between the main and
185 // worker scripts
186 push(data, valueheader(WorkerUndefined));
187 } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) {
188 uint length = array->getLength();
189 if (length > 0xFFFFFF) {
190 push(data, valueheader(WorkerUndefined));
191 return;
192 }
193 reserve(data, sizeof(quint32) + length * sizeof(quint32));
194 push(data, valueheader(WorkerArray, length));
195 ScopedValue val(scope);
196 for (uint ii = 0; ii < length; ++ii)
197 serialize(data, (val = array->get(ii)), engine);
198 } else if (v.isInteger()) {
199 reserve(data, 2 * sizeof(quint32));
200 push(data, valueheader(WorkerInt32));
201 push(data, (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, sizeof(quint32) + sizeof(double));
208 push(data, valueheader(WorkerNumber));
209 push(data, v.asDouble());
210 } else if (const QV4::DateObject *d = v.as<DateObject>()) {
211 reserve(data, sizeof(quint32) + sizeof(double));
212 push(data, valueheader(WorkerDate));
213 push(data, 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, valueheader(WorkerUndefined));
220 return;
221 }
222 int utf16size = ALIGN(length * sizeof(quint16));
223
224 reserve(data, sizeof(quint32) + utf16size);
225 push(data, valueheader(WorkerRegexp, flags));
226 push(data, (quint32)length);
227
228 int offset = data.size();
229 data.resize(data.size() + utf16size);
230 char *buffer = data.data() + offset;
231
232 memcpy(buffer, pattern.constData(), 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 *>(lm->property("agent"))) {
238 if (QMetaObject::invokeMethod(agent, "addref")) {
239 push(data, valueheader(WorkerListModel));
240 push(data, (void *)agent);
241 return;
242 }
243 }
244 }
245 // No other QObject's are allowed to be sent
246 push(data, valueheader(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(engine->id_length()))->toUInt32();
252 uint length = seqLength + 1;
253 if (length > 0xFFFFFF) {
254 push(data, valueheader(WorkerUndefined));
255 return;
256 }
257 reserve(data, sizeof(quint32) + length * sizeof(quint32));
258 push(data, valueheader(WorkerSequence, length));
259 serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
260 ScopedValue val(scope);
261 for (uint ii = 0; ii < seqLength; ++ii)
262 serialize(data, (val = o->get(ii)), engine); // sequence elements
263
264 return;
265 }
266 #endif
267 const QVariant variant = engine->toVariant(v, QMetaType::QUrl, false);
268 if (variant.userType() == QMetaType::QUrl) {
269 serializeString(data, variant.value<QUrl>().toString(), WorkerUrl);
270 return;
271 }
272
273 // regular object
274 QV4::ScopedValue val(scope, v);
275 QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val));
276 quint32 length = properties->getLength();
277 if (length > 0xFFFFFF) {
278 push(data, valueheader(WorkerUndefined));
279 return;
280 }
281 push(data, valueheader(WorkerObject, length));
282
283 QV4::ScopedValue s(scope);
284 for (quint32 ii = 0; ii < length; ++ii) {
285 s = properties->get(ii);
286 serialize(data, s, engine);
287
288 QV4::String *str = s->as<String>();
289 val = o->get(str);
290 if (scope.hasException())
291 scope.engine->catchException();
292
293 serialize(data, val, engine);
294 }
295 return;
296 } else {
297 push(data, valueheader(WorkerUndefined));
298 }
299 }
300
301 struct VariantRef
302 {
VariantRefVariantRef303 VariantRef() : obj(nullptr) {}
VariantRefVariantRef304 VariantRef(const VariantRef &r) : obj(r.obj) { addref(); }
VariantRefVariantRef305 VariantRef(QObject *a) : obj(a) { addref(); }
~VariantRefVariantRef306 ~VariantRef() { release(); }
307
operator =VariantRef308 VariantRef &operator=(const VariantRef &o) {
309 o.addref();
310 release();
311 obj = o.obj;
312 return *this;
313 }
314
addrefVariantRef315 void addref() const
316 {
317 if (obj)
318 QMetaObject::invokeMethod(obj, "addref");
319 }
320
releaseVariantRef321 void release() const
322 {
323 if (obj)
324 QMetaObject::invokeMethod(obj, "release");
325
326 }
327
328 QObject *obj;
329 };
330
331 QT_END_NAMESPACE
Q_DECLARE_METATYPE(VariantRef)332 Q_DECLARE_METATYPE(VariantRef)
333 Q_DECLARE_METATYPE(QV4::ExecutionEngine *)
334 QT_BEGIN_NAMESPACE
335
336 ReturnedValue 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(QUrl(qstr)))
360 : Encode(engine->newString(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(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(n, 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(QV4::Value::fromDouble(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, agent));
411 // ### Find a better solution then the ugly property
412 VariantRef ref(agent);
413 QVariant var = QVariant::fromValue(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(s, v);
417
418 QMetaObject::invokeMethod(agent, "release");
419 agent->setProperty("engine", QVariant::fromValue(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(seqLength);
433 for (quint32 ii = 0; ii < seqLength; ++ii) {
434 value = deserialize(data, engine);
435 array->arrayPut(ii, value);
436 }
437 array->setArrayLengthUnchecked(seqLength);
438 QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
439 return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded);
440 }
441 #endif
442 }
443 Q_ASSERT(!"Unreachable");
444 return QV4::Encode::undefined();
445 }
446
serialize(const QV4::Value & value,ExecutionEngine * engine)447 QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine)
448 {
449 QByteArray rv;
450 serialize(rv, value, engine);
451 return rv;
452 }
453
deserialize(const QByteArray & data,ExecutionEngine * engine)454 ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine)
455 {
456 const char *stream = data.constData();
457 return deserialize(stream, engine);
458 }
459
460 QT_END_NAMESPACE
461