2011-05-11 07:20:40 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
|
** All rights reserved.
|
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
|
**
|
|
|
|
** This file is part of the QtDeclarative module of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** GNU Lesser General Public License Usage
|
2011-08-04 06:20:18 +00:00
|
|
|
** 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
|
2011-08-04 06:20:18 +00:00
|
|
|
** 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.
|
|
|
|
**
|
2011-08-04 06:20:18 +00:00
|
|
|
** 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,
|
Add support for more sequence types
This commit adds support for more sequence types by adding a sequence
wrapper. This class enables conversion between v8::Array and C++
sequences of various types (currently just QList<int>, QList<qreal>,
QList<bool>, QList<QString>, QList<QUrl> and QStringList), but more
types can be added later if required).
When a JavaScript object is created from such a sequence, its
prototype object is set to the v8::Array prototype object. The
indexed setter, indexed getter, length and toString methods are
implemented directly or in terms of the underlying sequence resource.
Note that currently, sequences of ValueTypes are NOT supported, due to
the fact that operations like:
someObj.someValueTypeSequence[i].x = 5;
would not behave as required.
Task-number: QTBUG-20826
Task-number: QTBUG-21770
Change-Id: I36deb448fb0e87a32084a900e70a2604ff369309
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
2011-10-03 00:52:38 +00:00
|
|
|
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 {
|
Add support for more sequence types
This commit adds support for more sequence types by adding a sequence
wrapper. This class enables conversion between v8::Array and C++
sequences of various types (currently just QList<int>, QList<qreal>,
QList<bool>, QList<QString>, QList<QUrl> and QStringList), but more
types can be added later if required).
When a JavaScript object is created from such a sequence, its
prototype object is set to the v8::Array prototype object. The
indexed setter, indexed getter, length and toString methods are
implemented directly or in terms of the underlying sequence resource.
Note that currently, sequences of ValueTypes are NOT supported, due to
the fact that operations like:
someObj.someValueTypeSequence[i].x = 5;
would not behave as required.
Task-number: QTBUG-20826
Task-number: QTBUG-21770
Change-Id: I36deb448fb0e87a32084a900e70a2604ff369309
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
2011-10-03 00:52:38 +00:00
|
|
|
// 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());
|
|
|
|
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;
|
|
|
|
}
|
Add support for more sequence types
This commit adds support for more sequence types by adding a sequence
wrapper. This class enables conversion between v8::Array and C++
sequences of various types (currently just QList<int>, QList<qreal>,
QList<bool>, QList<QString>, QList<QUrl> and QStringList), but more
types can be added later if required).
When a JavaScript object is created from such a sequence, its
prototype object is set to the v8::Array prototype object. The
indexed setter, indexed getter, length and toString methods are
implemented directly or in terms of the underlying sequence resource.
Note that currently, sequences of ValueTypes are NOT supported, due to
the fact that operations like:
someObj.someValueTypeSequence[i].x = 5;
would not behave as required.
Task-number: QTBUG-20826
Task-number: QTBUG-21770
Change-Id: I36deb448fb0e87a32084a900e70a2604ff369309
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
2011-10-03 00:52:38 +00:00
|
|
|
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
|
|
|
|
|