diff options
Diffstat (limited to 'src/qml/qml/v8')
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 167 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8bindings.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8engine.cpp | 102 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8engine_p.h | 24 | ||||
-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 | 104 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8valuetypewrapper.cpp | 7 | ||||
-rw-r--r-- | src/qml/qml/v8/v8.pri | 2 |
10 files changed, 571 insertions, 117 deletions
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index c8f178db08..9c570d756b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -51,6 +51,7 @@ #include <private/qv8profilerservice_p.h> #include <private/qqmlprofilerservice_p.h> +#include <private/qqmlglobal_p.h> #include <QtCore/qstring.h> #include <QtCore/qdatetime.h> @@ -62,12 +63,6 @@ #include <QtCore/qfile.h> #include <QtCore/qcoreapplication.h> -#include <QtGui/qcolor.h> -#include <QtGui/qvector3d.h> -#include <QtGui/qvector4d.h> -#include <QtGui/qdesktopservices.h> -#include <QtGui/qfontdatabase.h> - QT_BEGIN_NAMESPACE namespace QQmlBuiltinFunctions { @@ -417,7 +412,7 @@ v8::Handle<v8::Value> rgba(const v8::Arguments &args) if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromRgbF(r, g, b, a))); + return V8ENGINE()->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a)); } /*! @@ -446,7 +441,7 @@ v8::Handle<v8::Value> hsla(const v8::Arguments &args) if (a < 0.0) a=0.0; if (a > 1.0) a=1.0; - return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromHslF(h, s, l, a))); + return V8ENGINE()->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a)); } /*! @@ -508,11 +503,13 @@ v8::Handle<v8::Value> vector3d(const v8::Arguments &args) if (args.Length() != 3) V8THROW_ERROR("Qt.vector(): Invalid arguments"); - double x = args[0]->ToNumber()->Value(); - double y = args[1]->ToNumber()->Value(); - double z = args[2]->ToNumber()->Value(); + float xyz[3]; + xyz[0] = args[0]->ToNumber()->Value(); + xyz[1] = args[1]->ToNumber()->Value(); + xyz[2] = args[2]->ToNumber()->Value(); - return V8ENGINE()->fromVariant(QVariant::fromValue(QVector3D(x, y, z))); + const void *params[] = { xyz }; + return V8ENGINE()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params)); } /*! @@ -524,12 +521,14 @@ v8::Handle<v8::Value> vector4d(const v8::Arguments &args) if (args.Length() != 4) V8THROW_ERROR("Qt.vector4d(): Invalid arguments"); - double x = args[0]->NumberValue(); - double y = args[1]->NumberValue(); - double z = args[2]->NumberValue(); - double w = args[3]->NumberValue(); + float xyzw[4]; + xyzw[0] = args[0]->ToNumber()->Value(); + xyzw[1] = args[1]->ToNumber()->Value(); + xyzw[2] = args[2]->ToNumber()->Value(); + xyzw[3] = args[3]->ToNumber()->Value(); - return V8ENGINE()->fromVariant(QVariant::fromValue(QVector4D(x, y, z, w))); + const void *params[] = { xyzw }; + return V8ENGINE()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params)); } /*! @@ -551,17 +550,14 @@ v8::Handle<v8::Value> lighter(const v8::Arguments &args) if (args.Length() != 1 && args.Length() != 2) V8THROW_ERROR("Qt.lighter(): Invalid arguments"); - QColor color; QVariant v = V8ENGINE()->toVariant(args[0], -1); - if (v.userType() == QVariant::Color) { - color = v.value<QColor>(); - } else if (v.userType() == QVariant::String) { + if (v.userType() == QVariant::String) { bool ok = false; - color = QQmlStringConverters::colorFromString(v.toString(), &ok); + v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { return v8::Null(); } - } else { + } else if (v.userType() != QVariant::Color) { return v8::Null(); } @@ -569,8 +565,7 @@ v8::Handle<v8::Value> lighter(const v8::Arguments &args) if (args.Length() == 2) factor = args[1]->ToNumber()->Value(); - color = color.lighter(int(qRound(factor*100.))); - return V8ENGINE()->fromVariant(QVariant::fromValue(color)); + return V8ENGINE()->fromVariant(QQml_colorProvider()->lighter(v, factor)); } /*! @@ -593,17 +588,14 @@ v8::Handle<v8::Value> darker(const v8::Arguments &args) if (args.Length() != 1 && args.Length() != 2) V8THROW_ERROR("Qt.darker(): Invalid arguments"); - QColor color; QVariant v = V8ENGINE()->toVariant(args[0], -1); - if (v.userType() == QVariant::Color) { - color = v.value<QColor>(); - } else if (v.userType() == QVariant::String) { + if (v.userType() == QVariant::String) { bool ok = false; - color = QQmlStringConverters::colorFromString(v.toString(), &ok); + v = QQmlStringConverters::colorFromString(v.toString(), &ok); if (!ok) { return v8::Null(); } - } else { + } else if (v.userType() != QVariant::Color) { return v8::Null(); } @@ -611,8 +603,7 @@ v8::Handle<v8::Value> darker(const v8::Arguments &args) if (args.Length() == 2) factor = args[1]->ToNumber()->Value(); - color = color.darker(int(qRound(factor*100.))); - return V8ENGINE()->fromVariant(QVariant::fromValue(color)); + return V8ENGINE()->fromVariant(QQml_colorProvider()->darker(v, factor)); } /*! @@ -645,53 +636,30 @@ v8::Handle<v8::Value> tint(const v8::Arguments &args) V8THROW_ERROR("Qt.tint(): Invalid arguments"); // base color - QColor color; - QVariant v = V8ENGINE()->toVariant(args[0], -1); - if (v.userType() == QVariant::Color) { - color = v.value<QColor>(); - } else if (v.userType() == QVariant::String) { + QVariant v1 = V8ENGINE()->toVariant(args[0], -1); + if (v1.userType() == QVariant::String) { bool ok = false; - color = QQmlStringConverters::colorFromString(v.toString(), &ok); + v1 = QQmlStringConverters::colorFromString(v1.toString(), &ok); if (!ok) { return v8::Null(); } - } else { + } else if (v1.userType() != QVariant::Color) { return v8::Null(); } // tint color - QColor tintColor; - v = V8ENGINE()->toVariant(args[1], -1); - if (v.userType() == QVariant::Color) { - tintColor = v.value<QColor>(); - } else if (v.userType() == QVariant::String) { + QVariant v2 = V8ENGINE()->toVariant(args[1], -1); + if (v2.userType() == QVariant::String) { bool ok = false; - tintColor = QQmlStringConverters::colorFromString(v.toString(), &ok); + v2 = QQmlStringConverters::colorFromString(v2.toString(), &ok); if (!ok) { return v8::Null(); } - } else { + } else if (v2.userType() != QVariant::Color) { return v8::Null(); } - // tint the base color and return the final color - QColor finalColor; - int a = tintColor.alpha(); - if (a == 0xFF) - finalColor = tintColor; - else if (a == 0x00) - finalColor = color; - else { - qreal a = tintColor.alphaF(); - qreal inv_a = 1.0 - a; - - finalColor.setRgbF(tintColor.redF() * a + color.redF() * inv_a, - tintColor.greenF() * a + color.greenF() * inv_a, - tintColor.blueF() * a + color.blueF() * inv_a, - a + inv_a * color.alphaF()); - } - - return V8ENGINE()->fromVariant(QVariant::fromValue(finalColor)); + return V8ENGINE()->fromVariant(QQml_colorProvider()->tint(v1, v2)); } /*! @@ -908,11 +876,8 @@ v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args) if (args.Length() != 1) return V8ENGINE()->fromVariant(false); - bool ret = false; -#ifndef QT_NO_DESKTOPSERVICES - ret = QDesktopServices::openUrl(V8ENGINE()->toVariant(resolvedUrl(args), -1).toUrl()); -#endif - return V8ENGINE()->fromVariant(ret); + QUrl url(V8ENGINE()->toVariant(resolvedUrl(args), -1).toUrl()); + return V8ENGINE()->fromVariant(QQml_guiProvider()->openUrlExternally(url)); } /*! @@ -945,8 +910,7 @@ v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args) if (args.Length() != 0) V8THROW_ERROR("Qt.fontFamilies(): Invalid arguments"); - QFontDatabase database; - return V8ENGINE()->fromVariant(database.families()); + return V8ENGINE()->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); } /*! @@ -1364,6 +1328,65 @@ v8::Handle<v8::Value> locale(const v8::Arguments &args) return QQmlLocale::locale(v8engine, code); } +/*! + \qmlmethod Qt::binding(function) + + Returns a JS object representing a binding expression which may be + assigned to any property in imperative code to cause a binding + assignment. + + There are two main use-cases for the function: firstly, in imperative + JavaScript code to cause a binding assignment: + + \snippet doc/src/snippets/declarative/qtBinding.1.qml 0 + + and secondly, when defining initial property values of dynamically + constructed objects (via Component.createObject() or + Loader.setSource()) as being bound to the result of an expression. + + For example, assuming the existence of a DynamicText component: + \snippet doc/src/snippets/declarative/DynamicText.qml 0 + + the output from: + \snippet doc/src/snippets/declarative/qtBinding.2.qml 0 + + and from: + \snippet doc/src/snippets/declarative/qtBinding.3.qml 0 + + should both be: + \code + Root text extra text + Modified root text extra text + Dynamic text extra text + Modified dynamic text extra text + \endcode + + This function cannot be used in property binding declarations + (see the documentation on \l{qml-javascript-assignment}{binding + declarations and binding assignments}) except when the result is + stored in an array bound to a var property. + + \snippet doc/src/snippets/declarative/qtBinding.4.qml 0 + + Note: in QtQuick 1.x, all function assignment was treated as + binding assignment, so the Qt.binding() function is new in + QtQuick 2.0. + + \since QtQuick 2.0 +*/ +v8::Handle<v8::Value> binding(const v8::Arguments &args) +{ + QString code; + if (args.Length() != 1) + V8THROW_ERROR("binding() requires 1 argument"); + if (!args[0]->IsFunction()) + V8THROW_TYPE("binding(): argument (binding expression) must be a function"); + + v8::Handle<v8::Object> rv = args[0]->ToObject()->Clone(); + rv->SetHiddenValue(V8ENGINE()->bindingFlagKey(), v8::Boolean::New(true)); + return rv; +} + } // namespace QQmlBuiltinFunctions QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index ddb1c64243..bbfe88a292 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -103,6 +103,7 @@ v8::Handle<v8::Value> qsTrId(const v8::Arguments &args); v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args); v8::Handle<v8::Value> stringArg(const v8::Arguments &args); v8::Handle<v8::Value> locale(const v8::Arguments &args); +v8::Handle<v8::Value> binding(const v8::Arguments &args); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8bindings.cpp b/src/qml/qml/v8/qv8bindings.cpp index 025854f1ac..65c395e013 100644 --- a/src/qml/qml/v8/qv8bindings.cpp +++ b/src/qml/qml/v8/qv8bindings.cpp @@ -144,10 +144,7 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) if (!watcher.wasDeleted() && !destroyedFlag()) { if (needsErrorData) { - QUrl url = parent->url(); - if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>")); - - delayedError()->error.setUrl(url); + delayedError()->error.setUrl(parent->url()); delayedError()->error.setLine(instruction->line); delayedError()->error.setColumn(-1); } diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 6b034c0ff6..8444d657ec 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -41,8 +41,6 @@ #include "qv8engine_p.h" -#include <QtGui/QGuiApplication> - #include "qv8contextwrapper_p.h" #include "qv8valuetypewrapper_p.h" #include "qv8sequencewrapper_p.h" @@ -52,14 +50,17 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmllist_p.h> #include <private/qqmlengine_p.h> -#include <private/qquickapplication_p.h> #include <private/qqmlxmlhttprequest_p.h> #include <private/qqmllocale_p.h> +#include <private/qqmlglobal_p.h> #include "qscript_impl_p.h" #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>) @@ -124,6 +125,7 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) , m_ownsV8Context(ownership == QJSEngine::CreateNewContext) , m_xmlHttpRequestData(0) , m_listModelData(0) + , m_application(0) { qMetaTypeId<QJSValue>(); qMetaTypeId<QList<int> >(); @@ -146,6 +148,8 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) QV8GCCallback::registerGcPrologueCallback(); m_strongReferencer = qPersistentNew(v8::Object::New()); + m_bindingFlagKey = qPersistentNew(v8::String::New("qml::binding")); + m_stringWrapper.init(); m_contextWrapper.init(this); m_qobjectWrapper.init(this); @@ -154,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")); @@ -181,6 +186,7 @@ QV8Engine::~QV8Engine() qPersistentDispose(m_strongReferencer); + m_jsonWrapper.destroy(); m_sequenceWrapper.destroy(); m_valueTypeWrapper.destroy(); m_variantWrapper.destroy(); @@ -190,6 +196,8 @@ QV8Engine::~QV8Engine() m_contextWrapper.destroy(); m_stringWrapper.destroy(); + qPersistentDispose(m_bindingFlagKey); + m_originalGlobalObject.destroy(); if (m_ownsV8Context) @@ -217,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) { @@ -248,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)); } } @@ -266,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; @@ -313,6 +329,7 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) if (type < QMetaType::User) { switch (QMetaType::Type(type)) { + case QMetaType::UnknownType: case QMetaType::Void: return v8::Undefined(); case QMetaType::Bool: @@ -364,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; @@ -532,9 +555,6 @@ QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value) -#include <QtGui/qvector3d.h> -#include <QtGui/qvector4d.h> - struct StaticQtMetaObject : public QObject { static const QMetaObject *get() @@ -597,10 +617,11 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this)); qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this)); + qt->Set(v8::String::New("binding"), V8FUNCTION(binding, this)); if (m_engine) { - qt->Set(v8::String::New("application"), newQObject(new QQuickApplication(m_engine))); - qt->Set(v8::String::New("inputMethod"), newQObject(qGuiApp->inputMethod(), CppOwnership)); + qt->SetAccessor(v8::String::New("application"), getApplication, 0, v8::External::New(this)); + qt->SetAccessor(v8::String::New("inputMethod"), getInputMethod, 0, v8::External::New(this)); qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); @@ -1089,6 +1110,7 @@ v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data) // check if it's one of the types we know switch (QMetaType::Type(type)) { + case QMetaType::UnknownType: case QMetaType::Void: return v8::Undefined(); case QMetaType::Bool: @@ -1149,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); @@ -1264,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: ; } @@ -1380,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) @@ -1501,6 +1571,22 @@ int QV8Engine::consoleCountHelper(const QString &file, int line, int column) return number; } +v8::Handle<v8::Value> QV8Engine::getApplication(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data())); + if (!engine->m_application) { + // Only allocate an application object once + engine->m_application = QQml_guiProvider()->application(engine->m_engine); + } + return engine->newQObject(engine->m_application); +} + +v8::Handle<v8::Value> QV8Engine::getInputMethod(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data())); + return engine->newQObject(QQml_guiProvider()->inputMethod(), CppOwnership); +} + void QV8GCCallback::registerGcPrologueCallback() { QV8Engine::ThreadData *td = QV8Engine::threadData(); diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index bc57b27085..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 @@ -357,6 +358,9 @@ public: // a QVariant wrapper inline v8::Handle<v8::Value> newQVariant(const QVariant &); + // Return the JS string key for the "function is a binding" flag + inline v8::Handle<v8::String> bindingFlagKey() const; + // Return the network access manager for this engine. By default this returns the network // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 // instance (like in WorkerScript). @@ -412,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); @@ -440,6 +451,9 @@ public: void addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle); void addRelationshipForGC(QObject *object, QObject *other); + static v8::Handle<v8::Value> getApplication(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> getInputMethod(v8::Local<v8::String> property, const v8::AccessorInfo &info); + struct ThreadData { ThreadData(); ~ThreadData(); @@ -461,6 +475,8 @@ protected: v8::Persistent<v8::Context> m_context; QScriptOriginalGlobalObject m_originalGlobalObject; + v8::Persistent<v8::String> m_bindingFlagKey; + QV8StringWrapper m_stringWrapper; QV8ContextWrapper m_contextWrapper; QV8QObjectWrapper m_qobjectWrapper; @@ -469,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; @@ -487,6 +504,8 @@ protected: QHash<QString, quint32> m_consoleCount; + QObject *m_application; + QVariant toBasicVariant(v8::Handle<v8::Value>); void initializeGlobal(v8::Handle<v8::Object>); @@ -609,6 +628,11 @@ v8::Handle<v8::Value> QV8Engine::newSequence(int sequenceType, QObject *object, return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded); } +v8::Handle<v8::String> QV8Engine::bindingFlagKey() const +{ + return m_bindingFlagKey; +} + // XXX Can this be made more optimal? It is called prior to resolving each and every // unqualified name in QV8ContextWrapper. bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string) 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 480c9f9d6f..176d4fb76f 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> @@ -449,7 +450,7 @@ static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, return retn; } - if (property.propType == QVariant::Invalid) { + if (property.propType == QMetaType::UnknownType) { QMetaProperty p = object->metaObject()->property(property.coreIndex); qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); @@ -524,7 +525,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject return v8::Handle<v8::Value>(); } - if (result->isFunction()) { + if (result->isFunction() && !result->isVMEProperty()) { if (result->isVMEFunction()) { return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex); } else if (result->isV8Function()) { @@ -584,23 +585,53 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert v8::Handle<v8::Value> value) { QQmlBinding *newBinding = 0; - if (value->IsFunction()) { - QQmlContextData *context = engine->callingContext(); - v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); - - v8::Local<v8::StackTrace> trace = - v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | - v8::StackTrace::kScriptName)); - v8::Local<v8::StackFrame> frame = trace->GetFrame(0); - int lineNumber = frame->GetLineNumber(); - int columnNumber = frame->GetColumn(); - QString url = engine->toString(frame->GetScriptName()); - - newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber); - newBinding->setTarget(object, *property, context); - newBinding->setEvaluateFlags(newBinding->evaluateFlags() | - QQmlBinding::RequiresThisObject); + if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) { + if (!property->isVMEProperty()) { + // XXX TODO: uncomment the following lines + // assigning a JS function to a non-var-property is not allowed. + //QString error = QLatin1String("Cannot assign JavaScript function to ") + + // QLatin1String(QMetaType::typeName(property->propType)); + //v8::ThrowException(v8::Exception::Error(engine->toString(error))); + //return; + // XXX TODO: remove the following transition behaviour + // Temporarily allow assignment of functions to non-var properties + // to mean binding assignment (as per old behaviour). + QQmlContextData *context = engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + QString url = engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber); + newBinding->setTarget(object, *property, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + qWarning("WARNING: function assignment is DEPRECATED and will be removed! Wrap RHS in Qt.binding(): %s:%d", qPrintable(engine->toString(frame->GetScriptName())), frame->GetLineNumber()); + } + } else { + // binding assignment. + QQmlContextData *context = engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + QString url = engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber); + newBinding->setTarget(object, *property, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } } QQmlAbstractBinding *oldBinding = @@ -608,6 +639,12 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert if (oldBinding) oldBinding->destroy(); + if (!newBinding && property->isVMEProperty()) { + // allow assignment of "special" values (null, undefined, function) to var properties + static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value); + return; + } + #define PROPERTY_STORE(cpptype, value) \ cpptype o = value; \ int status = -1; \ @@ -623,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)); @@ -654,10 +693,14 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert if (v.userType() == QVariant::Invalid) valueType = "null"; else valueType = QMetaType::typeName(v.userType()); + const char *targetTypeName = QMetaType::typeName(property->propType); + if (!targetTypeName) + targetTypeName = "an unregistered type"; + QString error = QLatin1String("Cannot assign ") + QLatin1String(valueType) + QLatin1String(" to ") + - QLatin1String(QMetaType::typeName(property->propType)); + QLatin1String(targetTypeName); v8::ThrowException(v8::Exception::Error(engine->toString(error))); } } @@ -1641,16 +1684,6 @@ static inline int QMetaObject_methods(const QMetaObject *metaObject) return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; } -static QByteArray QMetaMethod_name(const QMetaMethod &m) -{ - QByteArray sig = m.signature(); - int paren = sig.indexOf('('); - if (paren == -1) - return sig; - else - return sig.left(paren); -} - /*! Returns the next related method, if one, or 0. */ @@ -1679,9 +1712,9 @@ static const QQmlPropertyData * RelatedMethod(QObject *object, dummy.load(method); // Look for overloaded methods - QByteArray methodName = QMetaMethod_name(method); + QByteArray methodName = method.name(); for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { - if (methodName == QMetaMethod_name(mo->method(ii))) { + if (methodName == mo->method(ii).name()) { dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); dummy.overrideIndexIsProperty = 0; dummy.overrideIndex = ii; @@ -1795,7 +1828,7 @@ static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyD const QQmlPropertyData *candidate = &data; while (candidate) { error += QLatin1String("\n ") + - QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature()); + QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData()); candidate = RelatedMethod(object, candidate, dummy); } @@ -1964,7 +1997,7 @@ void *CallArgument::dataPtr() void CallArgument::initAsType(int callType) { if (type != 0) { cleanup(); type = 0; } - if (callType == 0) return; + if (callType == QMetaType::UnknownType) return; if (callType == qMetaTypeId<QJSValue>()) { qjsValuePtr = new (&allocData) QJSValue(); @@ -1990,6 +2023,9 @@ void CallArgument::initAsType(int callType) } else if (callType == qMetaTypeId<QQmlV8Handle>()) { type = callType; handlePtr = new (&allocData) QQmlV8Handle; + } else if (callType == QMetaType::Void) { + type = -1; + qvariantPtr = new (&allocData) QVariant(); } else { type = -1; qvariantPtr = new (&allocData) QVariant(callType, (void *)0); @@ -2044,6 +2080,8 @@ void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Val } else if (callType == qMetaTypeId<QQmlV8Handle>()) { handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value)); type = callType; + } else if (callType == QMetaType::Void) { + *qvariantPtr = QVariant(); } else { qvariantPtr = new (&allocData) QVariant(); type = -1; diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp index 7a3c675d49..cf2c13fce9 100644 --- a/src/qml/qml/v8/qv8valuetypewrapper.cpp +++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp @@ -324,6 +324,13 @@ v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property QQmlBinding *newBinding = 0; if (value->IsFunction()) { + if (value->ToObject()->GetHiddenValue(r->engine->bindingFlagKey()).IsEmpty()) { + // assigning a JS function to a non-var-property is not allowed. + QString error = QLatin1String("Cannot assign JavaScript function to value-type property"); + v8::ThrowException(v8::Exception::Error(r->engine->toString(error))); + return value; + } + QQmlContextData *context = r->engine->callingContext(); v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); 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 \ |