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>
This commit is contained in:
parent
31c5b237c4
commit
e5f45d9b57
|
@ -58,6 +58,9 @@
|
||||||
#include "qv8domerrors_p.h"
|
#include "qv8domerrors_p.h"
|
||||||
#include "qv8sqlerrors_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(QJSValue)
|
||||||
Q_DECLARE_METATYPE(QList<int>)
|
Q_DECLARE_METATYPE(QList<int>)
|
||||||
|
@ -155,6 +158,7 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
|
||||||
m_variantWrapper.init(this);
|
m_variantWrapper.init(this);
|
||||||
m_valueTypeWrapper.init(this);
|
m_valueTypeWrapper.init(this);
|
||||||
m_sequenceWrapper.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"));
|
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);
|
qPersistentDispose(m_strongReferencer);
|
||||||
|
|
||||||
|
m_jsonWrapper.destroy();
|
||||||
m_sequenceWrapper.destroy();
|
m_sequenceWrapper.destroy();
|
||||||
m_valueTypeWrapper.destroy();
|
m_valueTypeWrapper.destroy();
|
||||||
m_variantWrapper.destroy();
|
m_variantWrapper.destroy();
|
||||||
|
@ -220,6 +225,9 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
|
||||||
if (typeHint == QVariant::Bool)
|
if (typeHint == QVariant::Bool)
|
||||||
return QVariant(value->BooleanValue());
|
return QVariant(value->BooleanValue());
|
||||||
|
|
||||||
|
if (typeHint == QMetaType::QJsonValue)
|
||||||
|
return QVariant::fromValue(jsonValueFromJS(value));
|
||||||
|
|
||||||
if (value->IsObject()) {
|
if (value->IsObject()) {
|
||||||
QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
|
QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -251,6 +259,9 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
|
||||||
case QV8ObjectResource::SequenceType:
|
case QV8ObjectResource::SequenceType:
|
||||||
return m_sequenceWrapper.toVariant(r);
|
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);
|
return qVariantFromValue<QList<QObject*> >(list);
|
||||||
|
} else if (typeHint == QMetaType::QJsonArray) {
|
||||||
|
return QVariant::fromValue(jsonArrayFromJS(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool succeeded = false;
|
bool succeeded = false;
|
||||||
|
@ -368,6 +381,12 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
|
||||||
return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
|
return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
|
||||||
case QMetaType::QVariantMap:
|
case QMetaType::QVariantMap:
|
||||||
return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1152,6 +1171,15 @@ v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data)
|
||||||
case QMetaType::QVariant:
|
case QMetaType::QVariant:
|
||||||
result = variantToJS(*reinterpret_cast<const QVariant*>(data));
|
result = variantToJS(*reinterpret_cast<const QVariant*>(data));
|
||||||
break;
|
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:
|
default:
|
||||||
if (type == qMetaTypeId<QJSValue>()) {
|
if (type == qMetaTypeId<QJSValue>()) {
|
||||||
return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
|
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:
|
case QMetaType::QVariant:
|
||||||
*reinterpret_cast<QVariant*>(data) = variantFromJS(value);
|
*reinterpret_cast<QVariant*>(data) = variantFromJS(value);
|
||||||
return true;
|
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:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -1383,6 +1420,36 @@ QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value)
|
||||||
return variantMapFromJS(value->ToObject());
|
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,
|
bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
|
||||||
const QByteArray &targetType,
|
const QByteArray &targetType,
|
||||||
void **result)
|
void **result)
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
#include "qv8variantwrapper_p.h"
|
#include "qv8variantwrapper_p.h"
|
||||||
#include "qv8valuetypewrapper_p.h"
|
#include "qv8valuetypewrapper_p.h"
|
||||||
#include "qv8sequencewrapper_p.h"
|
#include "qv8sequencewrapper_p.h"
|
||||||
|
#include "qv8jsonwrapper_p.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
@ -415,6 +416,13 @@ public:
|
||||||
v8::Handle<v8::Value> variantToJS(const QVariant &value);
|
v8::Handle<v8::Value> variantToJS(const QVariant &value);
|
||||||
QVariant variantFromJS(v8::Handle<v8::Value> 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);
|
v8::Handle<v8::Value> metaTypeToJS(int type, const void *data);
|
||||||
bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data);
|
bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data);
|
||||||
|
|
||||||
|
@ -477,6 +485,7 @@ protected:
|
||||||
QV8VariantWrapper m_variantWrapper;
|
QV8VariantWrapper m_variantWrapper;
|
||||||
QV8ValueTypeWrapper m_valueTypeWrapper;
|
QV8ValueTypeWrapper m_valueTypeWrapper;
|
||||||
QV8SequenceWrapper m_sequenceWrapper;
|
QV8SequenceWrapper m_sequenceWrapper;
|
||||||
|
QV8JsonWrapper m_jsonWrapper;
|
||||||
|
|
||||||
v8::Persistent<v8::Function> m_getOwnPropertyNames;
|
v8::Persistent<v8::Function> m_getOwnPropertyNames;
|
||||||
v8::Persistent<v8::Function> m_freezeObject;
|
v8::Persistent<v8::Function> m_freezeObject;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include <private/qqmlexpression_p.h>
|
#include <private/qqmlexpression_p.h>
|
||||||
|
|
||||||
#include <QtQml/qjsvalue.h>
|
#include <QtQml/qjsvalue.h>
|
||||||
|
#include <QtCore/qjsonvalue.h>
|
||||||
#include <QtCore/qvarlengtharray.h>
|
#include <QtCore/qvarlengtharray.h>
|
||||||
#include <QtCore/qtimer.h>
|
#include <QtCore/qtimer.h>
|
||||||
#include <QtCore/qatomic.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);
|
QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
|
||||||
} else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
|
} else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
|
||||||
PROPERTY_STORE(QVariant, QVariant());
|
PROPERTY_STORE(QVariant, QVariant());
|
||||||
|
} else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
|
||||||
|
PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
|
||||||
} else if (value->IsUndefined()) {
|
} else if (value->IsUndefined()) {
|
||||||
QString error = QLatin1String("Cannot assign [undefined] to ") +
|
QString error = QLatin1String("Cannot assign [undefined] to ") +
|
||||||
QLatin1String(QMetaType::typeName(property->propType));
|
QLatin1String(QMetaType::typeName(property->propType));
|
||||||
|
|
|
@ -15,6 +15,7 @@ HEADERS += \
|
||||||
$$PWD/qv8variantwrapper_p.h \
|
$$PWD/qv8variantwrapper_p.h \
|
||||||
$$PWD/qv8variantresource_p.h \
|
$$PWD/qv8variantresource_p.h \
|
||||||
$$PWD/qv8valuetypewrapper_p.h \
|
$$PWD/qv8valuetypewrapper_p.h \
|
||||||
|
$$PWD/qv8jsonwrapper_p.h \
|
||||||
$$PWD/qv8include_p.h \
|
$$PWD/qv8include_p.h \
|
||||||
$$PWD/qv8worker_p.h \
|
$$PWD/qv8worker_p.h \
|
||||||
$$PWD/qv8bindings_p.h \
|
$$PWD/qv8bindings_p.h \
|
||||||
|
@ -33,6 +34,7 @@ SOURCES += \
|
||||||
$$PWD/qv8listwrapper.cpp \
|
$$PWD/qv8listwrapper.cpp \
|
||||||
$$PWD/qv8variantwrapper.cpp \
|
$$PWD/qv8variantwrapper.cpp \
|
||||||
$$PWD/qv8valuetypewrapper.cpp \
|
$$PWD/qv8valuetypewrapper.cpp \
|
||||||
|
$$PWD/qv8jsonwrapper.cpp \
|
||||||
$$PWD/qv8include.cpp \
|
$$PWD/qv8include.cpp \
|
||||||
$$PWD/qv8worker.cpp \
|
$$PWD/qv8worker.cpp \
|
||||||
$$PWD/qv8bindings.cpp \
|
$$PWD/qv8bindings.cpp \
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -0,0 +1 @@
|
||||||
|
[123]
|
|
@ -0,0 +1 @@
|
||||||
|
[true,false,null,"hello"]
|
|
@ -0,0 +1 @@
|
||||||
|
[{"a":42}]
|
|
@ -0,0 +1 @@
|
||||||
|
[[[42]],[]]
|
|
@ -0,0 +1 @@
|
||||||
|
false
|
|
@ -0,0 +1 @@
|
||||||
|
null
|
|
@ -0,0 +1 @@
|
||||||
|
123
|
|
@ -0,0 +1 @@
|
||||||
|
42.35
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1 @@
|
||||||
|
{"foo":123}
|
|
@ -0,0 +1 @@
|
||||||
|
{"a":true,"b":false,"c":null,"d":"hello"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"a":{"b":{"c":42}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"a":[],"b":[42],"c":{"d":null}}
|
|
@ -0,0 +1 @@
|
||||||
|
"hello"
|
|
@ -0,0 +1 @@
|
||||||
|
true
|
|
@ -0,0 +1,16 @@
|
||||||
|
CONFIG += testcase
|
||||||
|
TARGET = tst_qjsonbinding
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += tst_qjsonbinding.cpp
|
||||||
|
INCLUDEPATH += ../../shared
|
||||||
|
|
||||||
|
include (../../shared/util.pri)
|
||||||
|
|
||||||
|
# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage
|
||||||
|
# LIBS += -lgcov
|
||||||
|
|
||||||
|
TESTDATA = data/*
|
||||||
|
|
||||||
|
CONFIG += parallel_test
|
||||||
|
QT += core qml testlib
|
|
@ -0,0 +1,535 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/
|
||||||
|
**
|
||||||
|
** This file is part of the test suite 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 <QtTest/QtTest>
|
||||||
|
#include <QtQml/QtQml>
|
||||||
|
#include "../../shared/util.h"
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QJsonValue::Type)
|
||||||
|
|
||||||
|
class JsonPropertyObject : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QJsonValue value READ value WRITE setValue)
|
||||||
|
Q_PROPERTY(QJsonObject object READ object WRITE setObject)
|
||||||
|
Q_PROPERTY(QJsonArray array READ array WRITE setArray)
|
||||||
|
public:
|
||||||
|
QJsonValue value() const { return m_value; }
|
||||||
|
void setValue(const QJsonValue &v) { m_value = v; }
|
||||||
|
QJsonObject object() const { return m_object; }
|
||||||
|
void setObject(const QJsonObject &o) { m_object = o; }
|
||||||
|
QJsonArray array() const { return m_array; }
|
||||||
|
void setArray(const QJsonArray &a) { m_array = a; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonValue m_value;
|
||||||
|
QJsonObject m_object;
|
||||||
|
QJsonArray m_array;
|
||||||
|
};
|
||||||
|
|
||||||
|
class tst_qjsonbinding : public QQmlDataTest
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
tst_qjsonbinding() {}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void cppJsConversion_data();
|
||||||
|
void cppJsConversion();
|
||||||
|
|
||||||
|
void readValueProperty_data();
|
||||||
|
void readValueProperty();
|
||||||
|
void readObjectOrArrayProperty_data();
|
||||||
|
void readObjectOrArrayProperty();
|
||||||
|
|
||||||
|
void writeValueProperty_data();
|
||||||
|
void writeValueProperty();
|
||||||
|
void writeObjectOrArrayProperty_data();
|
||||||
|
void writeObjectOrArrayProperty();
|
||||||
|
|
||||||
|
void writeProperty_incompatibleType_data();
|
||||||
|
void writeProperty_incompatibleType();
|
||||||
|
|
||||||
|
void writeProperty_javascriptExpression_data();
|
||||||
|
void writeProperty_javascriptExpression();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray readAsUtf8(const QString &fileName);
|
||||||
|
static QJsonValue valueFromJson(const QByteArray &json);
|
||||||
|
|
||||||
|
void addPrimitiveDataTestFiles();
|
||||||
|
void addObjectDataTestFiles();
|
||||||
|
void addArrayDataTestFiles();
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray tst_qjsonbinding::readAsUtf8(const QString &fileName)
|
||||||
|
{
|
||||||
|
QFile file(testFile(fileName));
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
QTextStream stream(&file);
|
||||||
|
return stream.readAll().trimmed().toUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonValue tst_qjsonbinding::valueFromJson(const QByteArray &json)
|
||||||
|
{
|
||||||
|
if (json.isEmpty())
|
||||||
|
return QJsonValue(QJsonValue::Undefined);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(json);
|
||||||
|
if (!doc.isEmpty())
|
||||||
|
return doc.isObject() ? QJsonValue(doc.object()) : QJsonValue(doc.array());
|
||||||
|
|
||||||
|
// QJsonDocument::fromJson() only handles objects and arrays...
|
||||||
|
// Wrap the JSON inside a dummy object and extract the value.
|
||||||
|
QByteArray wrappedJson = "{\"prop\":" + json + "}";
|
||||||
|
doc = QJsonDocument::fromJson(wrappedJson);
|
||||||
|
Q_ASSERT(doc.isObject());
|
||||||
|
return doc.object().value("prop");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::addPrimitiveDataTestFiles()
|
||||||
|
{
|
||||||
|
QTest::newRow("true") << "true.json";
|
||||||
|
QTest::newRow("false") << "false.json";
|
||||||
|
|
||||||
|
QTest::newRow("null") << "null.json";
|
||||||
|
|
||||||
|
QTest::newRow("number.0") << "number.0.json";
|
||||||
|
QTest::newRow("number.1") << "number.1.json";
|
||||||
|
|
||||||
|
QTest::newRow("string.0") << "string.0.json";
|
||||||
|
|
||||||
|
QTest::newRow("undefined") << "empty.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::addObjectDataTestFiles()
|
||||||
|
{
|
||||||
|
QTest::newRow("object.0") << "object.0.json";
|
||||||
|
QTest::newRow("object.1") << "object.1.json";
|
||||||
|
QTest::newRow("object.2") << "object.2.json";
|
||||||
|
QTest::newRow("object.3") << "object.3.json";
|
||||||
|
QTest::newRow("object.4") << "object.4.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::addArrayDataTestFiles()
|
||||||
|
{
|
||||||
|
QTest::newRow("array.0") << "array.0.json";
|
||||||
|
QTest::newRow("array.1") << "array.1.json";
|
||||||
|
QTest::newRow("array.2") << "array.2.json";
|
||||||
|
QTest::newRow("array.3") << "array.3.json";
|
||||||
|
QTest::newRow("array.4") << "array.4.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::cppJsConversion_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("fileName");
|
||||||
|
|
||||||
|
addPrimitiveDataTestFiles();
|
||||||
|
addObjectDataTestFiles();
|
||||||
|
addArrayDataTestFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::cppJsConversion()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileName);
|
||||||
|
|
||||||
|
QByteArray json = readAsUtf8(fileName);
|
||||||
|
QJsonValue jsonValue = valueFromJson(json);
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
QJSValue stringify = eng.globalObject().property("JSON").property("stringify");
|
||||||
|
QVERIFY(stringify.isCallable());
|
||||||
|
|
||||||
|
{
|
||||||
|
QJSValue jsValue = eng.toScriptValue(jsonValue);
|
||||||
|
QVERIFY(!jsValue.isVariant());
|
||||||
|
switch (jsonValue.type()) {
|
||||||
|
case QJsonValue::Null:
|
||||||
|
QVERIFY(jsValue.isNull());
|
||||||
|
break;
|
||||||
|
case QJsonValue::Bool:
|
||||||
|
QVERIFY(jsValue.isBool());
|
||||||
|
QCOMPARE(jsValue.toBool(), jsonValue.toBool());
|
||||||
|
break;
|
||||||
|
case QJsonValue::Double:
|
||||||
|
QVERIFY(jsValue.isNumber());
|
||||||
|
QCOMPARE(jsValue.toNumber(), jsonValue.toDouble());
|
||||||
|
break;
|
||||||
|
case QJsonValue::String:
|
||||||
|
QVERIFY(jsValue.isString());
|
||||||
|
QCOMPARE(jsValue.toString(), jsonValue.toString());
|
||||||
|
break;
|
||||||
|
case QJsonValue::Array:
|
||||||
|
QVERIFY(jsValue.isArray());
|
||||||
|
break;
|
||||||
|
case QJsonValue::Object:
|
||||||
|
QVERIFY(jsValue.isObject());
|
||||||
|
break;
|
||||||
|
case QJsonValue::Undefined:
|
||||||
|
QVERIFY(jsValue.isUndefined());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsValue.isUndefined()) {
|
||||||
|
QVERIFY(json.isEmpty());
|
||||||
|
} else {
|
||||||
|
QJSValue stringified = stringify.call(QJSValueList() << jsValue);
|
||||||
|
QVERIFY(!stringified.isError());
|
||||||
|
QCOMPARE(stringified.toString().toUtf8(), json);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonValue roundtrip = qjsvalue_cast<QJsonValue>(jsValue);
|
||||||
|
// Workarounds for QTBUG-25164
|
||||||
|
if (jsonValue.isObject() && jsonValue.toObject().isEmpty())
|
||||||
|
QVERIFY(roundtrip.isObject() && roundtrip.toObject().isEmpty());
|
||||||
|
else if (jsonValue.isArray() && jsonValue.toArray().isEmpty())
|
||||||
|
QVERIFY(roundtrip.isArray() && roundtrip.toArray().isEmpty());
|
||||||
|
else
|
||||||
|
QCOMPARE(roundtrip, jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonValue.isObject()) {
|
||||||
|
QJsonObject jsonObject = jsonValue.toObject();
|
||||||
|
QJSValue jsObject = eng.toScriptValue(jsonObject);
|
||||||
|
QVERIFY(!jsObject.isVariant());
|
||||||
|
QVERIFY(jsObject.isObject());
|
||||||
|
|
||||||
|
QJSValue stringified = stringify.call(QJSValueList() << jsObject);
|
||||||
|
QVERIFY(!stringified.isError());
|
||||||
|
QCOMPARE(stringified.toString().toUtf8(), json);
|
||||||
|
|
||||||
|
QJsonObject roundtrip = qjsvalue_cast<QJsonObject>(jsObject);
|
||||||
|
QCOMPARE(roundtrip, jsonObject);
|
||||||
|
} else if (jsonValue.isArray()) {
|
||||||
|
QJsonArray jsonArray = jsonValue.toArray();
|
||||||
|
QJSValue jsArray = eng.toScriptValue(jsonArray);
|
||||||
|
QVERIFY(!jsArray.isVariant());
|
||||||
|
QVERIFY(jsArray.isArray());
|
||||||
|
|
||||||
|
QJSValue stringified = stringify.call(QJSValueList() << jsArray);
|
||||||
|
QVERIFY(!stringified.isError());
|
||||||
|
QCOMPARE(stringified.toString().toUtf8(), json);
|
||||||
|
|
||||||
|
QJsonArray roundtrip = qjsvalue_cast<QJsonArray>(jsArray);
|
||||||
|
QCOMPARE(roundtrip, jsonArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::readValueProperty_data()
|
||||||
|
{
|
||||||
|
cppJsConversion_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::readValueProperty()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileName);
|
||||||
|
|
||||||
|
QByteArray json = readAsUtf8(fileName);
|
||||||
|
QJsonValue jsonValue = valueFromJson(json);
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
obj.setValue(jsonValue);
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
QJSValue stringified = eng.evaluate(
|
||||||
|
"var v = obj.value; (typeof v == 'undefined') ? '' : JSON.stringify(v)");
|
||||||
|
QVERIFY(!stringified.isError());
|
||||||
|
QCOMPARE(stringified.toString().toUtf8(), json);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::readObjectOrArrayProperty_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("fileName");
|
||||||
|
|
||||||
|
addObjectDataTestFiles();
|
||||||
|
addArrayDataTestFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::readObjectOrArrayProperty()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileName);
|
||||||
|
|
||||||
|
QByteArray json = readAsUtf8(fileName);
|
||||||
|
QJsonValue jsonValue = valueFromJson(json);
|
||||||
|
QVERIFY(jsonValue.isObject() || jsonValue.isArray());
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
if (jsonValue.isObject())
|
||||||
|
obj.setObject(jsonValue.toObject());
|
||||||
|
else
|
||||||
|
obj.setArray(jsonValue.toArray());
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
|
||||||
|
QJSValue stringified = eng.evaluate(
|
||||||
|
QString::fromLatin1("JSON.stringify(obj.%0)").arg(
|
||||||
|
jsonValue.isObject() ? "object" : "array"));
|
||||||
|
QVERIFY(!stringified.isError());
|
||||||
|
QCOMPARE(stringified.toString().toUtf8(), json);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeValueProperty_data()
|
||||||
|
{
|
||||||
|
readValueProperty_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeValueProperty()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileName);
|
||||||
|
|
||||||
|
QByteArray json = readAsUtf8(fileName);
|
||||||
|
QJsonValue jsonValue = valueFromJson(json);
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
|
||||||
|
QJSValue fun = eng.evaluate(
|
||||||
|
"(function(json) {"
|
||||||
|
" void(obj.value = (json == '') ? undefined : JSON.parse(json));"
|
||||||
|
"})");
|
||||||
|
QVERIFY(fun.isCallable());
|
||||||
|
|
||||||
|
QVERIFY(obj.value().isNull());
|
||||||
|
QVERIFY(fun.call(QJSValueList() << QString::fromUtf8(json)).isUndefined());
|
||||||
|
|
||||||
|
// Workarounds for QTBUG-25164
|
||||||
|
if (jsonValue.isObject() && jsonValue.toObject().isEmpty())
|
||||||
|
QVERIFY(obj.value().isObject() && obj.value().toObject().isEmpty());
|
||||||
|
else if (jsonValue.isArray() && jsonValue.toArray().isEmpty())
|
||||||
|
QVERIFY(obj.value().isArray() && obj.value().toArray().isEmpty());
|
||||||
|
else
|
||||||
|
QCOMPARE(obj.value(), jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeObjectOrArrayProperty_data()
|
||||||
|
{
|
||||||
|
readObjectOrArrayProperty_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeObjectOrArrayProperty()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileName);
|
||||||
|
|
||||||
|
QByteArray json = readAsUtf8(fileName);
|
||||||
|
QJsonValue jsonValue = valueFromJson(json);
|
||||||
|
QVERIFY(jsonValue.isObject() || jsonValue.isArray());
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
|
||||||
|
QJSValue fun = eng.evaluate(
|
||||||
|
QString::fromLatin1(
|
||||||
|
"(function(json) {"
|
||||||
|
" void(obj.%0 = JSON.parse(json));"
|
||||||
|
"})").arg(jsonValue.isObject() ? "object" : "array")
|
||||||
|
);
|
||||||
|
QVERIFY(fun.isCallable());
|
||||||
|
|
||||||
|
QVERIFY(obj.object().isEmpty() && obj.array().isEmpty());
|
||||||
|
QVERIFY(fun.call(QJSValueList() << QString::fromUtf8(json)).isUndefined());
|
||||||
|
|
||||||
|
if (jsonValue.isObject())
|
||||||
|
QCOMPARE(obj.object(), jsonValue.toObject());
|
||||||
|
else
|
||||||
|
QCOMPARE(obj.array(), jsonValue.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeProperty_incompatibleType_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("property");
|
||||||
|
QTest::addColumn<QString>("expression");
|
||||||
|
|
||||||
|
QTest::newRow("value=function") << "value" << "(function(){})";
|
||||||
|
|
||||||
|
QTest::newRow("object=undefined") << "object" << "undefined";
|
||||||
|
QTest::newRow("object=null") << "object" << "null";
|
||||||
|
QTest::newRow("object=false") << "object" << "false";
|
||||||
|
QTest::newRow("object=true") << "object" << "true";
|
||||||
|
QTest::newRow("object=123") << "object" << "123";
|
||||||
|
QTest::newRow("object=42.35") << "object" << "42.35";
|
||||||
|
QTest::newRow("object='foo'") << "object" << "'foo'";
|
||||||
|
QTest::newRow("object=[]") << "object" << "[]";
|
||||||
|
QTest::newRow("object=function") << "object" << "(function(){})";
|
||||||
|
|
||||||
|
QTest::newRow("array=undefined") << "array" << "undefined";
|
||||||
|
QTest::newRow("array=null") << "array" << "null";
|
||||||
|
QTest::newRow("array=false") << "array" << "false";
|
||||||
|
QTest::newRow("array=true") << "array" << "true";
|
||||||
|
QTest::newRow("array=123") << "array" << "123";
|
||||||
|
QTest::newRow("array=42.35") << "array" << "42.35";
|
||||||
|
QTest::newRow("array='foo'") << "array" << "'foo'";
|
||||||
|
QTest::newRow("array={}") << "array" << "{}";
|
||||||
|
QTest::newRow("array=function") << "array" << "(function(){})";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeProperty_incompatibleType()
|
||||||
|
{
|
||||||
|
QFETCH(QString, property);
|
||||||
|
QFETCH(QString, expression);
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
|
||||||
|
QJSValue ret = eng.evaluate(QString::fromLatin1("obj.%0 = %1")
|
||||||
|
.arg(property).arg(expression));
|
||||||
|
QEXPECT_FAIL("value=function", "See 'XXX TODO: uncomment the following lines' in qv8qobjectwrapper.cpp", Abort);
|
||||||
|
QEXPECT_FAIL("object=function", "See 'XXX TODO: uncomment the following lines' in qv8qobjectwrapper.cpp", Abort);
|
||||||
|
QEXPECT_FAIL("array=function", "See 'XXX TODO: uncomment the following lines' in qv8qobjectwrapper.cpp", Abort);
|
||||||
|
QVERIFY(ret.isError());
|
||||||
|
QVERIFY(ret.toString().contains("Cannot assign"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeProperty_javascriptExpression_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("property");
|
||||||
|
QTest::addColumn<QString>("expression");
|
||||||
|
QTest::addColumn<QString>("expectedJson");
|
||||||
|
|
||||||
|
// Function properties should be omitted.
|
||||||
|
QTest::newRow("value = object with function property")
|
||||||
|
<< "value" << "{ foo: function() {} }" << "{}";
|
||||||
|
QTest::newRow("object = object with function property")
|
||||||
|
<< "object" << "{ foo: function() {} }" << "{}";
|
||||||
|
QTest::newRow("array = array with function property")
|
||||||
|
<< "array" << "[function() {}]" << "[]";
|
||||||
|
|
||||||
|
// Inherited properties should not be included.
|
||||||
|
QTest::newRow("value = object with inherited property")
|
||||||
|
<< "value" << "{ __proto__: { proto_foo: 123 } }"
|
||||||
|
<< "{}";
|
||||||
|
QTest::newRow("value = object with inherited property 2")
|
||||||
|
<< "value" << "{ foo: 123, __proto__: { proto_foo: 456 } }"
|
||||||
|
<< "{\"foo\":123}";
|
||||||
|
QTest::newRow("value = array with inherited property")
|
||||||
|
<< "value" << "(function() { var a = []; a.__proto__ = { proto_foo: 123 }; return a; })()"
|
||||||
|
<< "[]";
|
||||||
|
QTest::newRow("value = array with inherited property 2")
|
||||||
|
<< "value" << "(function() { var a = [10, 20]; a.__proto__ = { proto_foo: 123 }; return a; })()"
|
||||||
|
<< "[10,20]";
|
||||||
|
|
||||||
|
QTest::newRow("object = object with inherited property")
|
||||||
|
<< "object" << "{ __proto__: { proto_foo: 123 } }"
|
||||||
|
<< "{}";
|
||||||
|
QTest::newRow("object = object with inherited property 2")
|
||||||
|
<< "object" << "{ foo: 123, __proto__: { proto_foo: 456 } }"
|
||||||
|
<< "{\"foo\":123}";
|
||||||
|
QTest::newRow("array = array with inherited property")
|
||||||
|
<< "array" << "(function() { var a = []; a.__proto__ = { proto_foo: 123 }; return a; })()"
|
||||||
|
<< "[]";
|
||||||
|
QTest::newRow("array = array with inherited property 2")
|
||||||
|
<< "array" << "(function() { var a = [10, 20]; a.__proto__ = { proto_foo: 123 }; return a; })()"
|
||||||
|
<< "[10,20]";
|
||||||
|
|
||||||
|
// Non-enumerable properties should be included.
|
||||||
|
QTest::newRow("value = object with non-enumerable property")
|
||||||
|
<< "value" << "Object.defineProperty({}, 'foo', { value: 123, enumerable: false })"
|
||||||
|
<< "{\"foo\":123}";
|
||||||
|
QTest::newRow("object = object with non-enumerable property")
|
||||||
|
<< "object" << "Object.defineProperty({}, 'foo', { value: 123, enumerable: false })"
|
||||||
|
<< "{\"foo\":123}";
|
||||||
|
|
||||||
|
// Cyclic data structures are permitted, but the cyclic links become
|
||||||
|
// empty objects.
|
||||||
|
QTest::newRow("value = cyclic object")
|
||||||
|
<< "value" << "(function() { var o = { foo: 123 }; o.o = o; return o; })()"
|
||||||
|
<< "{\"foo\":123,\"o\":{}}";
|
||||||
|
QTest::newRow("value = cyclic array")
|
||||||
|
<< "value" << "(function() { var a = [10, 20]; a.push(a); return a; })()"
|
||||||
|
<< "[10,20,[]]";
|
||||||
|
QTest::newRow("object = cyclic object")
|
||||||
|
<< "object" << "(function() { var o = { bar: true }; o.o = o; return o; })()"
|
||||||
|
<< "{\"bar\":true,\"o\":{}}";
|
||||||
|
QTest::newRow("array = cyclic array")
|
||||||
|
<< "array" << "(function() { var a = [30, 40]; a.unshift(a); return a; })()"
|
||||||
|
<< "[[],30,40]";
|
||||||
|
|
||||||
|
// Properties with undefined value are excluded.
|
||||||
|
QTest::newRow("value = { foo: undefined }")
|
||||||
|
<< "value" << "{ foo: undefined }" << "{}";
|
||||||
|
QTest::newRow("value = { foo: undefined, bar: 123 }")
|
||||||
|
<< "value" << "{ foo: undefined, bar: 123 }" << "{\"bar\":123}";
|
||||||
|
QTest::newRow("value = { foo: 456, bar: undefined }")
|
||||||
|
<< "value" << "{ foo: 456, bar: undefined }" << "{\"foo\":456}";
|
||||||
|
|
||||||
|
QTest::newRow("object = { foo: undefined }")
|
||||||
|
<< "object" << "{ foo: undefined }" << "{}";
|
||||||
|
QTest::newRow("object = { foo: undefined, bar: 123 }")
|
||||||
|
<< "object" << "{ foo: undefined, bar: 123 }" << "{\"bar\":123}";
|
||||||
|
QTest::newRow("object = { foo: 456, bar: undefined }")
|
||||||
|
<< "object" << "{ foo: 456, bar: undefined }" << "{\"foo\":456}";
|
||||||
|
|
||||||
|
// QJsonArray::append() implicitly converts undefined values to null.
|
||||||
|
QTest::newRow("value = [undefined]")
|
||||||
|
<< "value" << "[undefined]" << "[null]";
|
||||||
|
QTest::newRow("value = [undefined, 10]")
|
||||||
|
<< "value" << "[undefined, 10]" << "[null,10]";
|
||||||
|
QTest::newRow("value = [10, undefined, 20]")
|
||||||
|
<< "value" << "[10, undefined, 20]" << "[10,null,20]";
|
||||||
|
|
||||||
|
QTest::newRow("array = [undefined]")
|
||||||
|
<< "array" << "[undefined]" << "[null]";
|
||||||
|
QTest::newRow("array = [undefined, 10]")
|
||||||
|
<< "array" << "[undefined, 10]" << "[null,10]";
|
||||||
|
QTest::newRow("array = [10, undefined, 20]")
|
||||||
|
<< "array" << "[10, undefined, 20]" << "[10,null,20]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_qjsonbinding::writeProperty_javascriptExpression()
|
||||||
|
{
|
||||||
|
QFETCH(QString, property);
|
||||||
|
QFETCH(QString, expression);
|
||||||
|
QFETCH(QString, expectedJson);
|
||||||
|
|
||||||
|
QJSEngine eng;
|
||||||
|
JsonPropertyObject obj;
|
||||||
|
eng.globalObject().setProperty("obj", eng.newQObject(&obj));
|
||||||
|
|
||||||
|
QJSValue ret = eng.evaluate(QString::fromLatin1("obj.%0 = %1; JSON.stringify(obj.%0)")
|
||||||
|
.arg(property).arg(expression));
|
||||||
|
QVERIFY(!ret.isError());
|
||||||
|
QCOMPARE(ret.toString(), expectedJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_qjsonbinding)
|
||||||
|
|
||||||
|
#include "tst_qjsonbinding.moc"
|
|
@ -8,6 +8,7 @@ PUBLICTESTS += \
|
||||||
qjsengine \
|
qjsengine \
|
||||||
qjsvalue \
|
qjsvalue \
|
||||||
qjsvalueiterator \
|
qjsvalueiterator \
|
||||||
|
qjsonbinding \
|
||||||
qmlmin \
|
qmlmin \
|
||||||
qmlplugindump \
|
qmlplugindump \
|
||||||
qqmlcomponent \
|
qqmlcomponent \
|
||||||
|
|
Loading…
Reference in New Issue