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