diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2012-03-23 18:14:29 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-04-16 09:25:06 +0200 |
commit | e5f45d9b57bb0542ec47e5a8a4e57388b6d59d35 (patch) | |
tree | 039622ca4f22c5be93cd491d506fe49427ae9672 /src | |
parent | 31c5b237c4f0eb6848c7d2e3ae84232c6f8f6d26 (diff) |
Add QJson support to QV8Engine
Make QV8Engine perform direct conversion between JavaScript values
and QJson{Value,Object,Array}.
This implementation always makes a deep clone of the
QJson{Object,Array} when converting to JS; it might make sense to add
a lazy conversion scheme for dealing with large objects.
Change-Id: Id0b65891a19515ce22f1e51fa8a28d9f3e595271
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
Reviewed-by: Jamey Hicks <jamey.hicks@nokia.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/qml/v8/qv8engine.cpp | 67 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8engine_p.h | 9 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8jsonwrapper.cpp | 183 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8jsonwrapper_p.h | 93 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8qobjectwrapper.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/v8/v8.pri | 2 |
6 files changed, 357 insertions, 0 deletions
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 2302d0e369..8444d657ec 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -58,6 +58,9 @@ #include "qv8domerrors_p.h" #include "qv8sqlerrors_p.h" +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> Q_DECLARE_METATYPE(QJSValue) Q_DECLARE_METATYPE(QList<int>) @@ -155,6 +158,7 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) m_variantWrapper.init(this); m_valueTypeWrapper.init(this); m_sequenceWrapper.init(this); + m_jsonWrapper.init(this); { v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); @@ -182,6 +186,7 @@ QV8Engine::~QV8Engine() qPersistentDispose(m_strongReferencer); + m_jsonWrapper.destroy(); m_sequenceWrapper.destroy(); m_valueTypeWrapper.destroy(); m_variantWrapper.destroy(); @@ -220,6 +225,9 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) if (typeHint == QVariant::Bool) return QVariant(value->BooleanValue()); + if (typeHint == QMetaType::QJsonValue) + return QVariant::fromValue(jsonValueFromJS(value)); + if (value->IsObject()) { QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); if (r) { @@ -251,6 +259,9 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) case QV8ObjectResource::SequenceType: return m_sequenceWrapper.toVariant(r); } + } else if (typeHint == QMetaType::QJsonObject + && !value->IsArray() && !value->IsFunction()) { + return QVariant::fromValue(jsonObjectFromJS(value)); } } @@ -269,6 +280,8 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) } return qVariantFromValue<QList<QObject*> >(list); + } else if (typeHint == QMetaType::QJsonArray) { + return QVariant::fromValue(jsonArrayFromJS(value)); } bool succeeded = false; @@ -368,6 +381,12 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); + case QMetaType::QJsonValue: + return jsonValueToJS(*reinterpret_cast<const QJsonValue *>(ptr)); + case QMetaType::QJsonObject: + return jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(ptr)); + case QMetaType::QJsonArray: + return jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(ptr)); default: break; @@ -1152,6 +1171,15 @@ v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data) case QMetaType::QVariant: result = variantToJS(*reinterpret_cast<const QVariant*>(data)); break; + case QMetaType::QJsonValue: + result = jsonValueToJS(*reinterpret_cast<const QJsonValue *>(data)); + break; + case QMetaType::QJsonObject: + result = jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(data)); + break; + case QMetaType::QJsonArray: + result = jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(data)); + break; default: if (type == qMetaTypeId<QJSValue>()) { return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this); @@ -1267,6 +1295,15 @@ bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data case QMetaType::QVariant: *reinterpret_cast<QVariant*>(data) = variantFromJS(value); return true; + case QMetaType::QJsonValue: + *reinterpret_cast<QJsonValue *>(data) = jsonValueFromJS(value); + return true; + case QMetaType::QJsonObject: + *reinterpret_cast<QJsonObject *>(data) = jsonObjectFromJS(value); + return true; + case QMetaType::QJsonArray: + *reinterpret_cast<QJsonArray *>(data) = jsonArrayFromJS(value); + return true; default: ; } @@ -1383,6 +1420,36 @@ QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value) return variantMapFromJS(value->ToObject()); } +v8::Handle<v8::Value> QV8Engine::jsonValueToJS(const QJsonValue &value) +{ + return m_jsonWrapper.fromJsonValue(value); +} + +QJsonValue QV8Engine::jsonValueFromJS(v8::Handle<v8::Value> value) +{ + return m_jsonWrapper.toJsonValue(value); +} + +v8::Local<v8::Object> QV8Engine::jsonObjectToJS(const QJsonObject &object) +{ + return m_jsonWrapper.fromJsonObject(object); +} + +QJsonObject QV8Engine::jsonObjectFromJS(v8::Handle<v8::Value> value) +{ + return m_jsonWrapper.toJsonObject(value); +} + +v8::Local<v8::Array> QV8Engine::jsonArrayToJS(const QJsonArray &array) +{ + return m_jsonWrapper.fromJsonArray(array); +} + +QJsonArray QV8Engine::jsonArrayFromJS(v8::Handle<v8::Value> value) +{ + return m_jsonWrapper.toJsonArray(value); +} + bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value, const QByteArray &targetType, void **result) diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 2eb3668501..1fc03d82e5 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -80,6 +80,7 @@ #include "qv8variantwrapper_p.h" #include "qv8valuetypewrapper_p.h" #include "qv8sequencewrapper_p.h" +#include "qv8jsonwrapper_p.h" QT_BEGIN_NAMESPACE @@ -415,6 +416,13 @@ public: v8::Handle<v8::Value> variantToJS(const QVariant &value); QVariant variantFromJS(v8::Handle<v8::Value> value); + v8::Handle<v8::Value> jsonValueToJS(const QJsonValue &value); + QJsonValue jsonValueFromJS(v8::Handle<v8::Value> value); + v8::Local<v8::Object> jsonObjectToJS(const QJsonObject &object); + QJsonObject jsonObjectFromJS(v8::Handle<v8::Value> value); + v8::Local<v8::Array> jsonArrayToJS(const QJsonArray &array); + QJsonArray jsonArrayFromJS(v8::Handle<v8::Value> value); + v8::Handle<v8::Value> metaTypeToJS(int type, const void *data); bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data); @@ -477,6 +485,7 @@ protected: QV8VariantWrapper m_variantWrapper; QV8ValueTypeWrapper m_valueTypeWrapper; QV8SequenceWrapper m_sequenceWrapper; + QV8JsonWrapper m_jsonWrapper; v8::Persistent<v8::Function> m_getOwnPropertyNames; v8::Persistent<v8::Function> m_freezeObject; diff --git a/src/qml/qml/v8/qv8jsonwrapper.cpp b/src/qml/qml/v8/qv8jsonwrapper.cpp new file mode 100644 index 0000000000..ff8cc4faba --- /dev/null +++ b/src/qml/qml/v8/qv8jsonwrapper.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8jsonwrapper_p.h" +#include "qv8engine_p.h" +#include "qjsconverter_impl_p.h" + +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> + +QT_BEGIN_NAMESPACE + +QV8JsonWrapper::QV8JsonWrapper() +: m_engine(0) +{ +} + +QV8JsonWrapper::~QV8JsonWrapper() +{ +} + +void QV8JsonWrapper::init(QV8Engine *engine) +{ + m_engine = engine; +} + +void QV8JsonWrapper::destroy() +{ +} + +v8::Handle<v8::Value> QV8JsonWrapper::fromJsonValue(const QJsonValue &value) +{ + if (value.isString()) + return QJSConverter::toString(value.toString()); + else if (value.isDouble()) + return v8::Number::New(value.toDouble()); + else if (value.isBool()) + return value.toBool() ? v8::True() : v8::False(); + else if (value.isArray()) + return fromJsonArray(value.toArray()); + else if (value.isObject()) + return fromJsonObject(value.toObject()); + else if (value.isNull()) + return v8::Null(); + else + return v8::Undefined(); +} + +QJsonValue QV8JsonWrapper::toJsonValue(v8::Handle<v8::Value> value) +{ + if (value->IsString()) + return QJsonValue(QJSConverter::toString(value.As<v8::String>())); + else if (value->IsNumber()) + return QJsonValue(value->NumberValue()); + else if (value->IsBoolean()) + return QJsonValue(value->BooleanValue()); + else if (value->IsArray()) + return toJsonArray(value.As<v8::Array>()); + else if (value->IsObject()) + return toJsonObject(value.As<v8::Object>()); + else if (value->IsNull()) + return QJsonValue(QJsonValue::Null); + else + return QJsonValue(QJsonValue::Undefined); +} + +v8::Local<v8::Object> QV8JsonWrapper::fromJsonObject(const QJsonObject &object) +{ + v8::Local<v8::Object> v8object = v8::Object::New(); + for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it) + v8object->Set(QJSConverter::toString(it.key()), fromJsonValue(it.value())); + return v8object; +} + +QJsonObject QV8JsonWrapper::toJsonObject(v8::Handle<v8::Value> value) +{ + QJsonObject result; + if (!value->IsObject() || value->IsArray() || value->IsFunction()) + return result; + + v8::Handle<v8::Object> v8object(value.As<v8::Object>()); + int hash = v8object->GetIdentityHash(); + if (m_visitedConversionObjects.contains(hash)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty object (and no error is thrown). + return result; + } + + m_visitedConversionObjects.insert(hash); + + v8::Local<v8::Array> propertyNames = m_engine->getOwnPropertyNames(v8object); + uint32_t length = propertyNames->Length(); + for (uint32_t i = 0; i < length; ++i) { + v8::Local<v8::Value> name = propertyNames->Get(i); + v8::Local<v8::Value> propertyValue = v8object->Get(name); + if (!propertyValue->IsFunction()) + result.insert(QJSConverter::toString(name->ToString()), toJsonValue(propertyValue)); + } + + m_visitedConversionObjects.remove(hash); + + return result; +} + +v8::Local<v8::Array> QV8JsonWrapper::fromJsonArray(const QJsonArray &array) +{ + int size = array.size(); + v8::Local<v8::Array> v8array = v8::Array::New(size); + for (int i = 0; i < size; i++) + v8array->Set(i, fromJsonValue(array.at(i))); + return v8array; +} + +QJsonArray QV8JsonWrapper::toJsonArray(v8::Handle<v8::Value> value) +{ + QJsonArray result; + if (!value->IsArray()) + return result; + + v8::Handle<v8::Array> v8array(value.As<v8::Array>()); + int hash = v8array->GetIdentityHash(); + if (m_visitedConversionObjects.contains(hash)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty array (and no error is thrown). + return result; + } + + m_visitedConversionObjects.insert(hash); + + uint32_t length = v8array->Length(); + for (uint32_t i = 0; i < length; ++i) { + v8::Local<v8::Value> element = v8array->Get(i); + if (!element->IsFunction()) + result.append(toJsonValue(element)); + } + + m_visitedConversionObjects.remove(hash); + + return result; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8jsonwrapper_p.h b/src/qml/qml/v8/qv8jsonwrapper_p.h new file mode 100644 index 0000000000..842a3aa5d5 --- /dev/null +++ b/src/qml/qml/v8/qv8jsonwrapper_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** 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. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8JSONWRAPPER_P_H +#define QV8JSONWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qset.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonValue; +class QJsonObject; +class QJsonArray; + +class QV8Engine; +class QV8JsonWrapper +{ +public: + QV8JsonWrapper(); + ~QV8JsonWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Handle<v8::Value> fromJsonValue(const QJsonValue &value); + QJsonValue toJsonValue(v8::Handle<v8::Value> value); + + v8::Local<v8::Object> fromJsonObject(const QJsonObject &object); + QJsonObject toJsonObject(v8::Handle<v8::Value> value); + + v8::Local<v8::Array> fromJsonArray(const QJsonArray &array); + QJsonArray toJsonArray(v8::Handle<v8::Value> value); + +private: + QV8Engine *m_engine; + QSet<int> m_visitedConversionObjects; +}; + +QT_END_NAMESPACE + +#endif // QV8JSONWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp index ce85725642..9a5aaca6e4 100644 --- a/src/qml/qml/v8/qv8qobjectwrapper.cpp +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -54,6 +54,7 @@ #include <private/qqmlexpression_p.h> #include <QtQml/qjsvalue.h> +#include <QtCore/qjsonvalue.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> @@ -659,6 +660,8 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a); } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) { PROPERTY_STORE(QVariant, QVariant()); + } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) { + PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); } else if (value->IsUndefined()) { QString error = QLatin1String("Cannot assign [undefined] to ") + QLatin1String(QMetaType::typeName(property->propType)); diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri index 7816c84b79..52b6bf480a 100644 --- a/src/qml/qml/v8/v8.pri +++ b/src/qml/qml/v8/v8.pri @@ -15,6 +15,7 @@ HEADERS += \ $$PWD/qv8variantwrapper_p.h \ $$PWD/qv8variantresource_p.h \ $$PWD/qv8valuetypewrapper_p.h \ + $$PWD/qv8jsonwrapper_p.h \ $$PWD/qv8include_p.h \ $$PWD/qv8worker_p.h \ $$PWD/qv8bindings_p.h \ @@ -33,6 +34,7 @@ SOURCES += \ $$PWD/qv8listwrapper.cpp \ $$PWD/qv8variantwrapper.cpp \ $$PWD/qv8valuetypewrapper.cpp \ + $$PWD/qv8jsonwrapper.cpp \ $$PWD/qv8include.cpp \ $$PWD/qv8worker.cpp \ $$PWD/qv8bindings.cpp \ |