Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8worker.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8worker_p.h"
43
44 #include <private/qdeclarativelistmodel_p.h>
45 #include <private/qdeclarativelistmodelworkeragent_p.h>
46
47 QT_BEGIN_NAMESPACE
48
49 // We allow the following JavaScript types to be passed between the main and 
50 // the secondary thread:
51 //    + undefined
52 //    + null
53 //    + Boolean
54 //    + String
55 //    + Function 
56 //    + Array
57 //    + "Simple" Objects
58 //    + Number
59 //    + Date
60 //    + RegExp
61 // <quint8 type><quint24 size><data>
62
63 enum Type {
64     WorkerUndefined,
65     WorkerNull,
66     WorkerTrue,
67     WorkerFalse,
68     WorkerString,
69     WorkerFunction,
70     WorkerArray,
71     WorkerObject,
72     WorkerInt32,
73     WorkerUint32,
74     WorkerNumber,
75     WorkerDate,
76     WorkerRegexp,
77     WorkerListModel,
78     WorkerSequence
79 };
80
81 static inline quint32 valueheader(Type type, quint32 size = 0)
82 {
83     return quint8(type) << 24 | (size & 0xFFFFFF);
84 }
85
86 static inline Type headertype(quint32 header)
87 {
88     return (Type)(header >> 24);
89 }
90
91 static inline quint32 headersize(quint32 header)
92 {
93     return header & 0xFFFFFF;
94 }
95
96 static inline void push(QByteArray &data, quint32 value)
97 {
98     data.append((const char *)&value, sizeof(quint32));
99 }
100
101 static inline void push(QByteArray &data, double value)
102 {
103     data.append((const char *)&value, sizeof(double));
104 }
105
106 static inline void push(QByteArray &data, void *ptr)
107 {
108     data.append((const char *)&ptr, sizeof(void *));
109 }
110
111 static inline void reserve(QByteArray &data, int extra)
112 {
113     data.reserve(data.size() + extra);
114 }
115
116 static inline quint32 popUint32(const char *&data)
117 {
118     quint32 rv = *((quint32 *)data);
119     data += sizeof(quint32);
120     return rv;
121 }
122
123 static inline double popDouble(const char *&data)
124 {
125     double rv = *((double *)data);
126     data += sizeof(double);
127     return rv;
128 }
129
130 static inline void *popPtr(const char *&data)
131 {
132     void *rv = *((void **)data);
133     data += sizeof(void *);
134     return rv;
135 }
136
137 // XXX TODO: Check that worker script is exception safe in the case of 
138 // serialization/deserialization failures
139
140 #define ALIGN(size) (((size) + 3) & ~3)
141 void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine)
142 {
143     if (v.IsEmpty()) {
144     } else if (v->IsUndefined()) {
145         push(data, valueheader(WorkerUndefined));
146     } else if (v->IsNull()) {
147         push(data, valueheader(WorkerNull));
148     } else if (v->IsTrue()) {
149         push(data, valueheader(WorkerTrue));
150     } else if (v->IsFalse()) {
151         push(data, valueheader(WorkerFalse));
152     } else if (v->IsString()) {
153         v8::Handle<v8::String> string = v->ToString();
154         int length = string->Length() + 1;
155         if (length > 0xFFFFFF) {
156             push(data, valueheader(WorkerUndefined));
157             return;
158         }
159         int utf16size = ALIGN(length * sizeof(uint16_t));
160
161         reserve(data, utf16size + sizeof(quint32));
162         push(data, valueheader(WorkerString, length));
163         
164         int offset = data.size();
165         data.resize(data.size() + utf16size);
166         char *buffer = data.data() + offset;
167
168         string->Write((uint16_t*)buffer);
169     } else if (v->IsFunction()) {
170         // XXX TODO: Implement passing function objects between the main and
171         // worker scripts
172         push(data, valueheader(WorkerUndefined));
173     } else if (v->IsArray()) {
174         v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(v);
175         uint32_t length = array->Length();
176         if (length > 0xFFFFFF) {
177             push(data, valueheader(WorkerUndefined));
178             return;
179         }
180         reserve(data, sizeof(quint32) + length * sizeof(quint32));
181         push(data, valueheader(WorkerArray, length));
182         for (uint32_t ii = 0; ii < length; ++ii)
183             serialize(data, array->Get(ii), engine);
184     } else if (v->IsInt32()) {
185         reserve(data, 2 * sizeof(quint32));
186         push(data, valueheader(WorkerInt32));
187         push(data, (quint32)v->Int32Value());
188     } else if (v->IsUint32()) {
189         reserve(data, 2 * sizeof(quint32));
190         push(data, valueheader(WorkerUint32));
191         push(data, v->Uint32Value());
192     } else if (v->IsNumber()) {
193         reserve(data, sizeof(quint32) + sizeof(double));
194         push(data, valueheader(WorkerNumber));
195         push(data, v->NumberValue());
196     } else if (v->IsDate()) {
197         reserve(data, sizeof(quint32) + sizeof(double));
198         push(data, valueheader(WorkerDate));
199         push(data, v8::Handle<v8::Date>::Cast(v)->NumberValue());
200     } else if (v->IsRegExp()) {
201         v8::Handle<v8::RegExp> regexp = v8::Handle<v8::RegExp>::Cast(v);
202         quint32 flags = regexp->GetFlags();
203         v8::Local<v8::String> source = regexp->GetSource();
204
205         int length = source->Length() + 1;
206         if (length > 0xFFFFFF) {
207             push(data, valueheader(WorkerUndefined));
208             return;
209         }
210         int utf16size = ALIGN(length * sizeof(uint16_t));
211
212         reserve(data, sizeof(quint32) + utf16size);
213         push(data, valueheader(WorkerRegexp, flags));
214         push(data, (quint32)length);
215         int offset = data.size();
216         data.resize(data.size() + utf16size);
217         char *buffer = data.data() + offset;
218
219         source->Write((uint16_t*)buffer);
220     } else if (v->IsObject() && !v->ToObject()->GetExternalResource()) {
221         v8::Handle<v8::Object> object = v->ToObject();
222         v8::Local<v8::Array> properties = engine->getOwnPropertyNames(object);
223         quint32 length = properties->Length();
224         if (length > 0xFFFFFF) {
225             push(data, valueheader(WorkerUndefined));
226             return;
227         }
228         push(data, valueheader(WorkerObject, length));
229         v8::TryCatch tc;
230         for (quint32 ii = 0; ii < length; ++ii) {
231             v8::Local<v8::String> str = properties->Get(ii)->ToString();
232             serialize(data, str, engine);
233
234             v8::Local<v8::Value> val = object->Get(str);
235             if (tc.HasCaught()) {
236                 serialize(data, v8::Undefined(), engine);
237                 tc.Reset();
238             } else {
239                 serialize(data, val, engine);
240             }
241         }
242     } else if (engine->isQObject(v)) {
243         // XXX TODO: Generalize passing objects between the main thread and worker scripts so 
244         // that others can trivially plug in their elements.
245         QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(engine->toQObject(v));
246         if (lm && lm->agent()) {
247             QDeclarativeListModelWorkerAgent *agent = lm->agent();
248             agent->addref();
249             push(data, valueheader(WorkerListModel));
250             push(data, (void *)agent);
251             return;
252         } 
253         // No other QObject's are allowed to be sent
254         push(data, valueheader(WorkerUndefined));
255     } else {
256         // we can convert sequences, but not other types with external data.
257         if (v->IsObject()) {
258             v8::Handle<v8::Object> seqObj = v->ToObject();
259             QV8ObjectResource *r = static_cast<QV8ObjectResource *>(seqObj->GetExternalResource());
260             if (r->resourceType() == QV8ObjectResource::SequenceType) {
261                 QVariant sequenceVariant = engine->sequenceWrapper()->toVariant(r);
262                 if (!sequenceVariant.isNull()) {
263                     // valid sequence.  we generate a length (sequence length + 1 for the sequence type)
264                     uint32_t seqLength = engine->sequenceWrapper()->sequenceLength(r);
265                     uint32_t length = seqLength + 1;
266                     if (length > 0xFFFFFF) {
267                         push(data, valueheader(WorkerUndefined));
268                         return;
269                     }
270                     reserve(data, sizeof(quint32) + length * sizeof(quint32));
271                     push(data, valueheader(WorkerSequence, length));
272                     serialize(data, v8::Integer::New(sequenceVariant.userType()), engine); // sequence type
273                     for (uint32_t ii = 0; ii < seqLength; ++ii) {
274                         serialize(data, seqObj->Get(ii), engine); // sequence elements
275                     }
276
277                     return;
278                 }
279             }
280         }
281
282         // not a sequence.
283         push(data, valueheader(WorkerUndefined));
284     }
285 }
286
287 v8::Handle<v8::Value> QV8Worker::deserialize(const char *&data, QV8Engine *engine)
288 {
289     quint32 header = popUint32(data);
290     Type type = headertype(header);
291
292     switch (type) {
293     case WorkerUndefined:
294         return v8::Undefined();
295     case WorkerNull:
296         return v8::Null();
297     case WorkerTrue:
298         return v8::True();
299     case WorkerFalse:
300         return v8::False();
301     case WorkerString:
302     {
303         quint32 size = headersize(header);
304         v8::Local<v8::String> string = v8::String::New((uint16_t*)data, size - 1);
305         data += ALIGN(size * sizeof(uint16_t));
306         return string;
307     }
308     case WorkerFunction:
309         Q_ASSERT(!"Unreachable");
310         break;
311     case WorkerArray:
312     {
313         quint32 size = headersize(header);
314         v8::Local<v8::Array> array = v8::Array::New(size);
315         for (quint32 ii = 0; ii < size; ++ii) {
316             array->Set(ii, deserialize(data, engine));
317         }
318         return array;
319     }
320     case WorkerObject:
321     {
322         quint32 size = headersize(header);
323         v8::Local<v8::Object> o = v8::Object::New();
324         for (quint32 ii = 0; ii < size; ++ii) {
325             v8::Handle<v8::Value> name = deserialize(data, engine);
326             v8::Handle<v8::Value> value = deserialize(data, engine);
327             o->Set(name, value);
328         }
329         return o;
330     }
331     case WorkerInt32:
332         return v8::Integer::New((qint32)popUint32(data));
333     case WorkerUint32:
334         return v8::Integer::NewFromUnsigned(popUint32(data));
335     case WorkerNumber:
336         return v8::Number::New(popDouble(data));
337     case WorkerDate:
338         return v8::Date::New(popDouble(data));
339     case WorkerRegexp:
340     {
341         quint32 flags = headersize(header);
342         quint32 length = popUint32(data);
343         v8::Local<v8::String> source = v8::String::New((uint16_t*)data, length - 1);
344         data += ALIGN(length * sizeof(uint16_t));
345         return v8::RegExp::New(source, (v8::RegExp::Flags)flags);
346     }
347     case WorkerListModel:
348     {
349         void *ptr = popPtr(data);
350         QDeclarativeListModelWorkerAgent *agent = (QDeclarativeListModelWorkerAgent *)ptr;
351         v8::Handle<v8::Value> rv = engine->newQObject(agent);
352         if (rv->IsObject()) {
353             QDeclarativeListModelWorkerAgent::VariantRef ref(agent);
354             QVariant var = qVariantFromValue(ref);
355             rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var));
356         }
357         agent->release();
358         agent->setV8Engine(engine);
359         return rv;
360     }
361     case WorkerSequence:
362     {
363         bool succeeded = false;
364         quint32 length = headersize(header);
365         quint32 seqLength = length - 1;
366         int sequenceType = deserialize(data, engine)->Int32Value();
367         v8::Local<v8::Array> array = v8::Array::New(seqLength);
368         for (quint32 ii = 0; ii < seqLength; ++ii)
369             array->Set(ii, deserialize(data, engine));
370         QVariant seqVariant = engine->sequenceWrapper()->toVariant(array, sequenceType, &succeeded);
371         return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded);
372     }
373     }
374     Q_ASSERT(!"Unreachable");
375     return v8::Undefined();
376 }
377
378 QByteArray QV8Worker::serialize(v8::Handle<v8::Value> value, QV8Engine *engine)
379 {
380     QByteArray rv;
381     serialize(rv, value, engine);
382     return rv;
383 }
384
385 v8::Handle<v8::Value> QV8Worker::deserialize(const QByteArray &data, QV8Engine *engine)
386 {
387     const char *stream = data.constData();
388     return deserialize(stream, engine);
389 }
390
391 QT_END_NAMESPACE
392