qtdeclarative/src/qml/jsruntime/qv4serialize.cpp

417 lines
14 KiB
C++
Raw Normal View History

2011-05-11 07:20:40 +00:00
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
2011-05-11 07:20:40 +00:00
**
** This file is part of the QtQml module of the Qt Toolkit.
2011-05-11 07:20:40 +00:00
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
2011-05-11 07:20:40 +00:00
** GNU Lesser General Public License Usage
** Alternatively, 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia 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.
2011-05-11 07:20:40 +00:00
**
**
2011-05-11 07:20:40 +00:00
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4serialize_p.h"
2011-05-11 07:20:40 +00:00
#include <private/qv8engine_p.h>
#include <private/qqmllistmodel_p.h>
#include <private/qqmllistmodelworkeragent_p.h>
2011-06-07 06:54:09 +00:00
#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4regexpobject_p.h>
#include <private/qv4sequenceobject_p.h>
#include <private/qv4objectproto_p.h>
2011-05-11 07:20:40 +00:00
QT_BEGIN_NAMESPACE
using namespace QV4;
2011-05-11 07:20:40 +00:00
// 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 Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine)
2011-05-11 07:20:40 +00:00
{
QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
QV4::Scope scope(v4);
if (v.isEmpty()) {
Q_ASSERT(!"Serialize: got empty value");
} else if (v.isUndefined()) {
2011-05-11 07:20:40 +00:00
push(data, valueheader(WorkerUndefined));
} else if (v.isNull()) {
2011-05-11 07:20:40 +00:00
push(data, valueheader(WorkerNull));
} else if (v.isBoolean()) {
push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse));
} else if (QV4::String *s = v.asString()) {
const QString &qstr = s->toQString();
int length = qstr.length();
2011-05-11 07:20:40 +00:00
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;
memcpy(buffer, qstr.constData(), length*sizeof(QChar));
} else if (v.asFunctionObject()) {
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 (QV4::ArrayObject *array = v.asArrayObject()) {
uint32_t length = array->arrayLength();
2011-05-11 07:20:40 +00:00
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, QV4::Value::fromReturnedValue(array->getIndexed(ii)), engine);
} else if (v.isInteger()) {
2011-05-11 07:20:40 +00:00
reserve(data, 2 * sizeof(quint32));
push(data, valueheader(WorkerInt32));
push(data, (quint32)v.integerValue());
// } else if (v->IsUint32()) {
// reserve(data, 2 * sizeof(quint32));
// push(data, valueheader(WorkerUint32));
// push(data, v->Uint32Value());
} else if (v.isNumber()) {
2011-05-11 07:20:40 +00:00
reserve(data, sizeof(quint32) + sizeof(double));
push(data, valueheader(WorkerNumber));
push(data, v.asDouble());
} else if (QV4::DateObject *d = v.asDateObject()) {
2011-05-11 07:20:40 +00:00
reserve(data, sizeof(quint32) + sizeof(double));
push(data, valueheader(WorkerDate));
push(data, d->value.asDouble());
} else if (QV4::RegExpObject *re = v.as<RegExpObject>()) {
quint32 flags = re->flags();
QString pattern = re->source();
int length = pattern.length() + 1;
2011-05-11 07:20:40 +00:00
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);
2011-05-11 07:20:40 +00:00
int offset = data.size();
data.resize(data.size() + utf16size);
char *buffer = data.data() + offset;
memcpy(buffer, pattern.constData(), length*sizeof(QChar));
} else if (QV4::QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
2011-06-09 04:40:44 +00:00
// that others can trivially plug in their elements.
QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
2011-06-07 06:54:09 +00:00
if (lm && lm->agent()) {
QQmlListModelWorkerAgent *agent = lm->agent();
2011-06-07 06:54:09 +00:00
agent->addref();
push(data, valueheader(WorkerListModel));
push(data, (void *)agent);
return;
}
2011-06-07 06:54:09 +00:00
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (QV4::Object *o = v.asObject()) {
if (o->isListType()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint32_t seqLength = ScopedValue(scope, o->get(v4->id_length))->toUInt32();
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, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
for (uint32_t ii = 0; ii < seqLength; ++ii)
serialize(data, QV4::Value::fromReturnedValue(o->getIndexed(ii)), engine); // sequence elements
return;
}
// regular object
QV4::ScopedValue val(scope, v);
QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4, val));
quint32 length = properties->arrayLength();
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
}
push(data, valueheader(WorkerObject, length));
QV4::ScopedValue s(scope);
QV4::ScopedString str(scope);
for (quint32 ii = 0; ii < length; ++ii) {
s = properties->getIndexed(ii);
serialize(data, s, engine);
QV4::ExecutionContext *ctx = v4->current;
try {
str = s->asString();
val = o->get(str);
} catch (QV4::Exception &e) {
e.accept(ctx);
}
serialize(data, val, engine);
}
return;
} else {
2011-05-11 07:20:40 +00:00
push(data, valueheader(WorkerUndefined));
}
}
ReturnedValue Serialize::deserialize(const char *&data, QV8Engine *engine)
2011-05-11 07:20:40 +00:00
{
quint32 header = popUint32(data);
Type type = headertype(header);
QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
Scope scope(v4);
2011-05-11 07:20:40 +00:00
switch (type) {
case WorkerUndefined:
return QV4::Encode::undefined();
2011-05-11 07:20:40 +00:00
case WorkerNull:
return QV4::Encode::null();
2011-05-11 07:20:40 +00:00
case WorkerTrue:
return QV4::Encode(true);
2011-05-11 07:20:40 +00:00
case WorkerFalse:
return QV4::Encode(false);
2011-05-11 07:20:40 +00:00
case WorkerString:
{
quint32 size = headersize(header);
QString qstr((QChar *)data, size);
2011-05-11 07:20:40 +00:00
data += ALIGN(size * sizeof(uint16_t));
return QV4::Value::fromString(v4->newString(qstr)).asReturnedValue();
2011-05-11 07:20:40 +00:00
}
case WorkerFunction:
Q_ASSERT(!"Unreachable");
break;
case WorkerArray:
{
quint32 size = headersize(header);
Scoped<ArrayObject> a(scope, v4->newArrayObject());
ScopedValue v(scope);
2011-05-11 07:20:40 +00:00
for (quint32 ii = 0; ii < size; ++ii) {
v = deserialize(data, engine);
a->putIndexed(ii, v);
2011-05-11 07:20:40 +00:00
}
return a.asReturnedValue();
2011-05-11 07:20:40 +00:00
}
case WorkerObject:
{
quint32 size = headersize(header);
Scoped<Object> o(scope, v4->newObject());
ScopedValue name(scope);
ScopedString n(scope);
ScopedValue value(scope);
2011-05-11 07:20:40 +00:00
for (quint32 ii = 0; ii < size; ++ii) {
name = deserialize(data, engine);
value = deserialize(data, engine);
n = name.asReturnedValue();
o->put(n, value);
2011-05-11 07:20:40 +00:00
}
return o.asReturnedValue();
2011-05-11 07:20:40 +00:00
}
case WorkerInt32:
return QV4::Encode((qint32)popUint32(data));
2011-05-11 07:20:40 +00:00
case WorkerUint32:
return QV4::Encode(popUint32(data));
2011-05-11 07:20:40 +00:00
case WorkerNumber:
return QV4::Encode(popDouble(data));
2011-05-11 07:20:40 +00:00
case WorkerDate:
return QV4::Encode(v4->newDateObject(QV4::Value::fromDouble(popDouble(data))));
2011-05-11 07:20:40 +00:00
case WorkerRegexp:
{
quint32 flags = headersize(header);
quint32 length = popUint32(data);
QString pattern = QString((QChar *)data, length - 1);
2011-05-11 07:20:40 +00:00
data += ALIGN(length * sizeof(uint16_t));
return Encode(v4->newRegExpObject(pattern, flags));
2011-05-11 07:20:40 +00:00
}
2011-06-07 06:54:09 +00:00
case WorkerListModel:
{
void *ptr = popPtr(data);
QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr;
QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, agent));
// ### Find a better solution then the ugly property
QQmlListModelWorkerAgent::VariantRef ref(agent);
QVariant var = qVariantFromValue(ref);
QV4::ScopedValue v(scope, engine->fromVariant((var)));
QV4::ScopedString s(scope, v4->newString("__qml:hidden:ref"));
rv->asObject()->defineReadonlyProperty(s, v);
2011-06-07 06:54:09 +00:00
agent->release();
agent->setV8Engine(engine);
return rv.asReturnedValue();
2011-06-07 06:54:09 +00:00
}
case WorkerSequence:
{
ScopedValue value(scope);
bool succeeded = false;
quint32 length = headersize(header);
quint32 seqLength = length - 1;
value = deserialize(data, engine);
int sequenceType = value->integerValue();
Scoped<ArrayObject> array(scope, v4->newArrayObject());
array->arrayReserve(seqLength);
for (quint32 ii = 0; ii < seqLength; ++ii) {
value = deserialize(data, engine);
array->arrayData[ii].value = value;
array->arrayDataLen = ii + 1;
}
array->setArrayLengthUnchecked(seqLength);
QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
return QV4::SequencePrototype::fromVariant(v4, seqVariant, &succeeded);
}
2011-05-11 07:20:40 +00:00
}
Q_ASSERT(!"Unreachable");
return QV4::Encode::undefined();
2011-05-11 07:20:40 +00:00
}
QByteArray Serialize::serialize(const QV4::Value &value, QV8Engine *engine)
2011-05-11 07:20:40 +00:00
{
QByteArray rv;
serialize(rv, value, engine);
return rv;
}
ReturnedValue Serialize::deserialize(const QByteArray &data, QV8Engine *engine)
2011-05-11 07:20:40 +00:00
{
const char *stream = data.constData();
return deserialize(stream, engine);
}
QT_END_NAMESPACE