qtdeclarative/src/declarative/qml/v8/qv8worker.cpp

393 lines
13 KiB
C++
Raw Normal View History

2011-05-11 07:20:40 +00:00
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
2011-05-11 07:20:40 +00:00
** All rights reserved.
** Contact: http://www.qt-project.org/
2011-05-11 07:20:40 +00:00
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
2011-05-11 07:20:40 +00:00
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
2011-05-11 07:20:40 +00:00
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
2011-05-11 07:20:40 +00:00
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv8worker_p.h"
2011-06-07 06:54:09 +00:00
#include <private/qdeclarativelistmodel_p.h>
#include <private/qdeclarativelistmodelworkeragent_p.h>
2011-05-11 07:20:40 +00:00
QT_BEGIN_NAMESPACE
// We allow the following JavaScript types to be passed between the main and
// the secondary thread:
// + undefined
// + null
// + Boolean
// + String
// + Function
// + Array
// + "Simple" Objects
// + Number
// + Date
// + RegExp
// <quint8 type><quint24 size><data>
enum Type {
WorkerUndefined,
WorkerNull,
WorkerTrue,
WorkerFalse,
WorkerString,
WorkerFunction,
WorkerArray,
WorkerObject,
WorkerInt32,
WorkerUint32,
WorkerNumber,
WorkerDate,
2011-06-07 06:54:09 +00:00
WorkerRegexp,
WorkerListModel,
WorkerSequence
2011-05-11 07:20:40 +00:00
};
static inline quint32 valueheader(Type type, quint32 size = 0)
{
return quint8(type) << 24 | (size & 0xFFFFFF);
}
static inline Type headertype(quint32 header)
{
return (Type)(header >> 24);
}
static inline quint32 headersize(quint32 header)
{
return header & 0xFFFFFF;
}
static inline void push(QByteArray &data, quint32 value)
{
data.append((const char *)&value, sizeof(quint32));
}
static inline void push(QByteArray &data, double value)
{
data.append((const char *)&value, sizeof(double));
}
2011-06-07 06:54:09 +00:00
static inline void push(QByteArray &data, void *ptr)
{
data.append((const char *)&ptr, sizeof(void *));
}
2011-05-11 07:20:40 +00:00
static inline void reserve(QByteArray &data, int extra)
{
data.reserve(data.size() + extra);
}
static inline quint32 popUint32(const char *&data)
{
quint32 rv = *((quint32 *)data);
data += sizeof(quint32);
return rv;
}
static inline double popDouble(const char *&data)
{
double rv = *((double *)data);
data += sizeof(double);
return rv;
}
2011-06-07 06:54:09 +00:00
static inline void *popPtr(const char *&data)
{
void *rv = *((void **)data);
data += sizeof(void *);
return rv;
}
2011-06-09 04:40:44 +00:00
// XXX TODO: Check that worker script is exception safe in the case of
// serialization/deserialization failures
2011-05-11 07:20:40 +00:00
#define ALIGN(size) (((size) + 3) & ~3)
void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine)
{
if (v.IsEmpty()) {
} else if (v->IsUndefined()) {
push(data, valueheader(WorkerUndefined));
} else if (v->IsNull()) {
push(data, valueheader(WorkerNull));
} else if (v->IsTrue()) {
push(data, valueheader(WorkerTrue));
} else if (v->IsFalse()) {
push(data, valueheader(WorkerFalse));
} else if (v->IsString()) {
v8::Handle<v8::String> string = v->ToString();
int length = string->Length() + 1;
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
int utf16size = ALIGN(length * sizeof(uint16_t));
reserve(data, utf16size + sizeof(quint32));
push(data, valueheader(WorkerString, length));
int offset = data.size();
data.resize(data.size() + utf16size);
char *buffer = data.data() + offset;
string->Write((uint16_t*)buffer);
} else if (v->IsFunction()) {
2011-06-09 04:40:44 +00:00
// XXX TODO: Implement passing function objects between the main and
// worker scripts
2011-05-11 07:20:40 +00:00
push(data, valueheader(WorkerUndefined));
} else if (v->IsArray()) {
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(v);
uint32_t length = array->Length();
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerArray, length));
for (uint32_t ii = 0; ii < length; ++ii)
serialize(data, array->Get(ii), engine);
} else if (v->IsInt32()) {
reserve(data, 2 * sizeof(quint32));
push(data, valueheader(WorkerInt32));
push(data, (quint32)v->Int32Value());
} else if (v->IsUint32()) {
reserve(data, 2 * sizeof(quint32));
push(data, valueheader(WorkerUint32));
push(data, v->Uint32Value());
} else if (v->IsNumber()) {
reserve(data, sizeof(quint32) + sizeof(double));
push(data, valueheader(WorkerNumber));
push(data, v->NumberValue());
} else if (v->IsDate()) {
reserve(data, sizeof(quint32) + sizeof(double));
push(data, valueheader(WorkerDate));
push(data, v8::Handle<v8::Date>::Cast(v)->NumberValue());
} else if (v->IsRegExp()) {
v8::Handle<v8::RegExp> regexp = v8::Handle<v8::RegExp>::Cast(v);
quint32 flags = regexp->GetFlags();
v8::Local<v8::String> source = regexp->GetSource();
int length = source->Length() + 1;
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
int utf16size = ALIGN(length * sizeof(uint16_t));
reserve(data, sizeof(quint32) + utf16size);
push(data, valueheader(WorkerRegexp, flags));
push(data, (quint32)length);
int offset = data.size();
data.resize(data.size() + utf16size);
char *buffer = data.data() + offset;
source->Write((uint16_t*)buffer);
} else if (v->IsObject() && !v->ToObject()->GetExternalResource()) {
v8::Handle<v8::Object> object = v->ToObject();
v8::Local<v8::Array> properties = engine->getOwnPropertyNames(object);
quint32 length = properties->Length();
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
push(data, valueheader(WorkerObject, length));
v8::TryCatch tc;
for (quint32 ii = 0; ii < length; ++ii) {
v8::Local<v8::String> str = properties->Get(ii)->ToString();
serialize(data, str, engine);
v8::Local<v8::Value> val = object->Get(str);
if (tc.HasCaught()) {
serialize(data, v8::Undefined(), engine);
tc.Reset();
} else {
serialize(data, val, engine);
}
}
2011-06-07 06:54:09 +00:00
} else if (engine->isQObject(v)) {
2011-06-09 04:40:44 +00:00
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
// that others can trivially plug in their elements.
2011-06-07 06:54:09 +00:00
QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(engine->toQObject(v));
if (lm && lm->agent()) {
QDeclarativeListModelWorkerAgent *agent = lm->agent();
agent->addref();
push(data, valueheader(WorkerListModel));
push(data, (void *)agent);
return;
}
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
2011-05-11 07:20:40 +00:00
} else {
// we can convert sequences, but not other types with external data.
if (v->IsObject()) {
v8::Handle<v8::Object> seqObj = v->ToObject();
QV8ObjectResource *r = static_cast<QV8ObjectResource *>(seqObj->GetExternalResource());
if (r->resourceType() == QV8ObjectResource::SequenceType) {
QVariant sequenceVariant = engine->sequenceWrapper()->toVariant(r);
if (!sequenceVariant.isNull()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint32_t seqLength = engine->sequenceWrapper()->sequenceLength(r);
uint32_t length = seqLength + 1;
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerSequence, length));
serialize(data, v8::Integer::New(sequenceVariant.userType()), engine); // sequence type
for (uint32_t ii = 0; ii < seqLength; ++ii) {
serialize(data, seqObj->Get(ii), engine); // sequence elements
}
return;
}
}
}
// not a sequence.
2011-05-11 07:20:40 +00:00
push(data, valueheader(WorkerUndefined));
}
}
v8::Handle<v8::Value> QV8Worker::deserialize(const char *&data, QV8Engine *engine)
{
quint32 header = popUint32(data);
Type type = headertype(header);
switch (type) {
case WorkerUndefined:
return v8::Undefined();
case WorkerNull:
return v8::Null();
case WorkerTrue:
return v8::True();
case WorkerFalse:
return v8::False();
case WorkerString:
{
quint32 size = headersize(header);
v8::Local<v8::String> string = v8::String::New((uint16_t*)data, size - 1);
data += ALIGN(size * sizeof(uint16_t));
return string;
}
case WorkerFunction:
Q_ASSERT(!"Unreachable");
break;
case WorkerArray:
{
quint32 size = headersize(header);
v8::Local<v8::Array> array = v8::Array::New(size);
for (quint32 ii = 0; ii < size; ++ii) {
array->Set(ii, deserialize(data, engine));
}
return array;
}
case WorkerObject:
{
quint32 size = headersize(header);
v8::Local<v8::Object> o = v8::Object::New();
for (quint32 ii = 0; ii < size; ++ii) {
v8::Handle<v8::Value> name = deserialize(data, engine);
v8::Handle<v8::Value> value = deserialize(data, engine);
o->Set(name, value);
}
return o;
}
case WorkerInt32:
return v8::Integer::New((qint32)popUint32(data));
case WorkerUint32:
return v8::Integer::NewFromUnsigned(popUint32(data));
case WorkerNumber:
return v8::Number::New(popDouble(data));
case WorkerDate:
return v8::Date::New(popDouble(data));
case WorkerRegexp:
{
quint32 flags = headersize(header);
quint32 length = popUint32(data);
v8::Local<v8::String> source = v8::String::New((uint16_t*)data, length - 1);
data += ALIGN(length * sizeof(uint16_t));
return v8::RegExp::New(source, (v8::RegExp::Flags)flags);
}
2011-06-07 06:54:09 +00:00
case WorkerListModel:
{
void *ptr = popPtr(data);
QDeclarativeListModelWorkerAgent *agent = (QDeclarativeListModelWorkerAgent *)ptr;
v8::Handle<v8::Value> rv = engine->newQObject(agent);
if (rv->IsObject()) {
QDeclarativeListModelWorkerAgent::VariantRef ref(agent);
QVariant var = qVariantFromValue(ref);
rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var));
}
agent->release();
agent->setV8Engine(engine);
return rv;
}
case WorkerSequence:
{
bool succeeded = false;
quint32 length = headersize(header);
quint32 seqLength = length - 1;
int sequenceType = deserialize(data, engine)->Int32Value();
v8::Local<v8::Array> array = v8::Array::New(seqLength);
for (quint32 ii = 0; ii < seqLength; ++ii)
array->Set(ii, deserialize(data, engine));
QVariant seqVariant = engine->sequenceWrapper()->toVariant(array, sequenceType, &succeeded);
return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded);
}
2011-05-11 07:20:40 +00:00
}
Q_ASSERT(!"Unreachable");
return v8::Undefined();
}
QByteArray QV8Worker::serialize(v8::Handle<v8::Value> value, QV8Engine *engine)
{
QByteArray rv;
serialize(rv, value, engine);
return rv;
}
v8::Handle<v8::Value> QV8Worker::deserialize(const QByteArray &data, QV8Engine *engine)
{
const char *stream = data.constData();
return deserialize(stream, engine);
}
QT_END_NAMESPACE