1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8worker_p.h"
44 #include <private/qdeclarativelistmodel_p.h>
45 #include <private/qdeclarativelistmodelworkeragent_p.h>
49 // We allow the following JavaScript types to be passed between the main and
50 // the secondary thread:
61 // <quint8 type><quint24 size><data>
81 static inline quint32 valueheader(Type type, quint32 size = 0)
83 return quint8(type) << 24 | (size & 0xFFFFFF);
86 static inline Type headertype(quint32 header)
88 return (Type)(header >> 24);
91 static inline quint32 headersize(quint32 header)
93 return header & 0xFFFFFF;
96 static inline void push(QByteArray &data, quint32 value)
98 data.append((const char *)&value, sizeof(quint32));
101 static inline void push(QByteArray &data, double value)
103 data.append((const char *)&value, sizeof(double));
106 static inline void push(QByteArray &data, void *ptr)
108 data.append((const char *)&ptr, sizeof(void *));
111 static inline void reserve(QByteArray &data, int extra)
113 data.reserve(data.size() + extra);
116 static inline quint32 popUint32(const char *&data)
118 quint32 rv = *((quint32 *)data);
119 data += sizeof(quint32);
123 static inline double popDouble(const char *&data)
125 double rv = *((double *)data);
126 data += sizeof(double);
130 static inline void *popPtr(const char *&data)
132 void *rv = *((void **)data);
133 data += sizeof(void *);
137 // XXX TODO: Check that worker script is exception safe in the case of
138 // serialization/deserialization failures
140 #define ALIGN(size) (((size) + 3) & ~3)
141 void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine)
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));
159 int utf16size = ALIGN(length * sizeof(uint16_t));
161 reserve(data, utf16size + sizeof(quint32));
162 push(data, valueheader(WorkerString, length));
164 int offset = data.size();
165 data.resize(data.size() + utf16size);
166 char *buffer = data.data() + offset;
168 string->Write((uint16_t*)buffer);
169 } else if (v->IsFunction()) {
170 // XXX TODO: Implement passing function objects between the main and
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));
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();
205 int length = source->Length() + 1;
206 if (length > 0xFFFFFF) {
207 push(data, valueheader(WorkerUndefined));
210 int utf16size = ALIGN(length * sizeof(uint16_t));
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;
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));
228 push(data, valueheader(WorkerObject, length));
230 for (quint32 ii = 0; ii < length; ++ii) {
231 v8::Local<v8::String> str = properties->Get(ii)->ToString();
232 serialize(data, str, engine);
234 v8::Local<v8::Value> val = object->Get(str);
235 if (tc.HasCaught()) {
236 serialize(data, v8::Undefined(), engine);
239 serialize(data, val, engine);
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();
249 push(data, valueheader(WorkerListModel));
250 push(data, (void *)agent);
253 // No other QObject's are allowed to be sent
254 push(data, valueheader(WorkerUndefined));
256 // we can convert sequences, but not other types with external data.
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));
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
283 push(data, valueheader(WorkerUndefined));
287 v8::Handle<v8::Value> QV8Worker::deserialize(const char *&data, QV8Engine *engine)
289 quint32 header = popUint32(data);
290 Type type = headertype(header);
293 case WorkerUndefined:
294 return v8::Undefined();
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));
309 Q_ASSERT(!"Unreachable");
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));
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);
332 return v8::Integer::New((qint32)popUint32(data));
334 return v8::Integer::NewFromUnsigned(popUint32(data));
336 return v8::Number::New(popDouble(data));
338 return v8::Date::New(popDouble(data));
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);
347 case WorkerListModel:
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));
358 agent->setV8Engine(engine);
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);
374 Q_ASSERT(!"Unreachable");
375 return v8::Undefined();
378 QByteArray QV8Worker::serialize(v8::Handle<v8::Value> value, QV8Engine *engine)
381 serialize(rv, value, engine);
385 v8::Handle<v8::Value> QV8Worker::deserialize(const QByteArray &data, QV8Engine *engine)
387 const char *stream = data.constData();
388 return deserialize(stream, engine);