aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlvaluetypewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlvaluetypewrapper.cpp')
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp401
1 files changed, 401 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
new file mode 100644
index 0000000000..388025dbc4
--- /dev/null
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlvaluetypewrapper_p.h"
+#include <private/qv8engine_p.h>
+
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmlcontextwrapper_p.h>
+
+#include <private/qv4engine_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4variantobject_p.h>
+#include <private/qv4qmlextensions_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(QmlValueTypeWrapper);
+
+class QmlValueTypeReference : public QmlValueTypeWrapper
+{
+public:
+ QmlValueTypeReference(QV8Engine *engine);
+
+ QQmlGuard<QObject> object;
+ int property;
+};
+
+class QmlValueTypeCopy : public QmlValueTypeWrapper
+{
+public:
+ QmlValueTypeCopy(QV8Engine *engine);
+
+ QVariant value;
+};
+
+QmlValueTypeWrapper::QmlValueTypeWrapper(QV8Engine *engine, ObjectType objectType)
+ : Object(QV8Engine::getV4(engine)), objectType(objectType)
+{
+ v8 = engine;
+ vtbl = &static_vtbl;
+}
+
+QmlValueTypeWrapper::~QmlValueTypeWrapper()
+{
+}
+
+QmlValueTypeReference::QmlValueTypeReference(QV8Engine *engine)
+: QmlValueTypeWrapper(engine, Reference)
+{
+}
+
+QmlValueTypeCopy::QmlValueTypeCopy(QV8Engine *engine)
+: QmlValueTypeWrapper(engine, Copy)
+{
+}
+
+
+static bool readReferenceValue(const QmlValueTypeReference *reference)
+{
+ // A reference resource may be either a "true" reference (eg, to a QVector3D property)
+ // or a "variant" reference (eg, to a QVariant property which happens to contain a value-type).
+ QMetaProperty writebackProperty = reference->object->metaObject()->property(reference->property);
+ if (writebackProperty.userType() == QMetaType::QVariant) {
+ // variant-containing-value-type reference
+ QVariant variantReferenceValue;
+ reference->type->readVariantValue(reference->object, reference->property, &variantReferenceValue);
+ int variantReferenceType = variantReferenceValue.userType();
+ if (variantReferenceType != reference->type->userType()) {
+ // This is a stale VariantReference. That is, the variant has been
+ // overwritten with a different type in the meantime.
+ // We need to modify this reference to the updated value type, if
+ // possible, or return false if it is not a value type.
+ if (QQmlValueTypeFactory::isValueType(variantReferenceType)) {
+ reference->type = QQmlValueTypeFactory::valueType(variantReferenceType);
+ if (!reference->type) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ reference->type->setValue(variantReferenceValue);
+ } else {
+ // value-type reference
+ reference->type->read(reference->object, reference->property);
+ }
+ return true;
+}
+
+void QmlValueTypeWrapper::initProto(ExecutionEngine *v4)
+{
+ if (v4->qmlExtensions()->valueTypeWrapperPrototype)
+ return;
+
+ Object *o = v4->newObject();
+ o->defineDefaultProperty(v4, QStringLiteral("toString"), method_toString, 1);
+ v4->qmlExtensions()->valueTypeWrapperPrototype = o;
+}
+
+Value QmlValueTypeWrapper::create(QV8Engine *v8, QObject *object, int property, QQmlValueType *type)
+{
+ ExecutionEngine *v4 = QV8Engine::getV4(v8);
+ initProto(v4);
+
+ QmlValueTypeReference *r = new (v4->memoryManager) QmlValueTypeReference(v8);
+ r->prototype = v4->qmlExtensions()->valueTypeWrapperPrototype;
+ r->type = type; r->object = object; r->property = property;
+ return Value::fromObject(r);
+}
+
+Value QmlValueTypeWrapper::create(QV8Engine *v8, const QVariant &value, QQmlValueType *type)
+{
+ ExecutionEngine *v4 = QV8Engine::getV4(v8);
+ initProto(v4);
+
+ QmlValueTypeCopy *r = new (v4->memoryManager) QmlValueTypeCopy(v8);
+ r->prototype = v4->qmlExtensions()->valueTypeWrapperPrototype;
+ r->type = type; r->value = value;
+ return Value::fromObject(r);
+}
+
+QVariant QmlValueTypeWrapper::toVariant() const
+{
+ if (objectType == QmlValueTypeWrapper::Reference) {
+ const QmlValueTypeReference *reference = static_cast<const QmlValueTypeReference *>(this);
+
+ if (reference->object && readReferenceValue(reference)) {
+ return reference->type->value();
+ } else {
+ return QVariant();
+ }
+ } else {
+ Q_ASSERT(objectType == QmlValueTypeWrapper::Copy);
+ return static_cast<const QmlValueTypeCopy *>(this)->value;
+ }
+}
+
+void QmlValueTypeWrapper::destroy(Managed *that)
+{
+ QmlValueTypeWrapper *w = that->as<QmlValueTypeWrapper>();
+ assert(w);
+ if (w->objectType == Reference)
+ static_cast<QmlValueTypeReference *>(w)->~QmlValueTypeReference();
+ else
+ static_cast<QmlValueTypeCopy *>(w)->~QmlValueTypeCopy();
+}
+
+bool QmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
+{
+ QV4::QmlValueTypeWrapper *lv = m->as<QmlValueTypeWrapper>();
+ assert(lv);
+
+ if (QV4::VariantObject *rv = other->as<VariantObject>())
+ return lv->isEqual(rv->data);
+
+ if (QV4::QmlValueTypeWrapper *v = other->as<QmlValueTypeWrapper>())
+ return lv->isEqual(v->toVariant());
+
+ return false;
+}
+
+bool QmlValueTypeWrapper::isEqual(const QVariant& value)
+{
+ if (objectType == QmlValueTypeWrapper::Reference) {
+ QmlValueTypeReference *reference = static_cast<QmlValueTypeReference *>(this);
+ if (reference->object && readReferenceValue(reference)) {
+ return reference->type->isEqual(value);
+ } else {
+ return false;
+ }
+ } else {
+ Q_ASSERT(objectType == QmlValueTypeWrapper::Copy);
+ QmlValueTypeCopy *copy = static_cast<QmlValueTypeCopy *>(this);
+ type->setValue(copy->value);
+ if (type->isEqual(value))
+ return true;
+ return (value == copy->value);
+ }
+}
+
+Value QmlValueTypeWrapper::method_toString(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ ctx->throwTypeError();
+ QmlValueTypeWrapper *w = o->as<QmlValueTypeWrapper>();
+ if (!w)
+ ctx->throwTypeError();
+
+ if (w->objectType == QmlValueTypeWrapper::Reference) {
+ QmlValueTypeReference *reference = static_cast<QmlValueTypeReference *>(w);
+ if (reference->object && readReferenceValue(reference)) {
+ return w->v8->toString(w->type->toString());
+ } else {
+ return QV4::Value::undefinedValue();
+ }
+ } else {
+ Q_ASSERT(w->objectType == QmlValueTypeWrapper::Copy);
+ QmlValueTypeCopy *copy = static_cast<QmlValueTypeCopy *>(w);
+ w->type->setValue(copy->value);
+ return w->v8->toString(w->type->toString());
+ }
+}
+
+Value QmlValueTypeWrapper::get(Managed *m, String *name, bool *hasProperty)
+{
+ QmlValueTypeWrapper *r = m->as<QmlValueTypeWrapper>();
+ QV4::ExecutionEngine *v4 = m->engine();
+ if (!r)
+ v4->current->throwTypeError();
+
+ QHashedV4String propertystring(Value::fromString(name));
+
+ // Note: readReferenceValue() can change the reference->type.
+ if (r->objectType == QmlValueTypeWrapper::Reference) {
+ QmlValueTypeReference *reference = static_cast<QmlValueTypeReference *>(r);
+
+ if (!reference->object || !readReferenceValue(reference))
+ return Value::undefinedValue();
+
+ } else {
+ Q_ASSERT(r->objectType == QmlValueTypeWrapper::Copy);
+
+ QmlValueTypeCopy *copy = static_cast<QmlValueTypeCopy *>(r);
+
+ r->type->setValue(copy->value);
+ }
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = 0;
+ {
+ QQmlData *ddata = QQmlData::get(r->type, false);
+ if (ddata && ddata->propertyCache)
+ result = ddata->propertyCache->property(propertystring, 0, 0);
+ else
+ result = QQmlPropertyCache::property(r->v8->engine(), r->type, propertystring, 0, local);
+ }
+
+ if (!result)
+ return Object::get(m, name, hasProperty);
+
+ if (result->isFunction()) {
+ // calling a Q_INVOKABLE function of a value type
+ QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4);
+ return QV4::QObjectWrapper::getQmlProperty(v4->current, qmlContext, r->type, name, QV4::QObjectWrapper::IgnoreRevision);
+ }
+
+#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
+ if (result->propType == metatype) { \
+ cpptype v; \
+ void *args[] = { &v, 0 }; \
+ r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \
+ return constructor(v); \
+ }
+
+ // These four types are the most common used by the value type wrappers
+ VALUE_TYPE_LOAD(QMetaType::QReal, qreal, QV4::Value::fromDouble);
+ VALUE_TYPE_LOAD(QMetaType::Int, int, QV4::Value::fromInt32);
+ VALUE_TYPE_LOAD(QMetaType::QString, QString, r->v8->toString);
+ VALUE_TYPE_LOAD(QMetaType::Bool, bool, QV4::Value::fromBoolean);
+
+ QVariant v(result->propType, (void *)0);
+ void *args[] = { v.data(), 0 };
+ r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args);
+ return r->v8->fromVariant(v);
+#undef VALUE_TYPE_ACCESSOR
+}
+
+void QmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
+{
+ QmlValueTypeWrapper *r = m->as<QmlValueTypeWrapper>();
+ ExecutionEngine *v4 = m->engine();
+ if (!r)
+ v4->current->throwTypeError();
+
+ QByteArray propName = name->toQString().toUtf8();
+ if (r->objectType == QmlValueTypeWrapper::Reference) {
+ QmlValueTypeReference *reference = static_cast<QmlValueTypeReference *>(r);
+ QMetaProperty writebackProperty = reference->object->metaObject()->property(reference->property);
+
+ if (!reference->object || !writebackProperty.isWritable() || !readReferenceValue(reference))
+ return;
+
+ // we lookup the index after readReferenceValue() since it can change the reference->type.
+ int index = r->type->metaObject()->indexOfProperty(propName.constData());
+ if (index == -1)
+ return;
+ QMetaProperty p = r->type->metaObject()->property(index);
+
+ QQmlBinding *newBinding = 0;
+
+ QV4::FunctionObject *f = value.asFunctionObject();
+ if (f) {
+ if (!f->bindingKeyFlag) {
+ // assigning a JS function to a non-var-property is not allowed.
+ QString error = QLatin1String("Cannot assign JavaScript function to value-type property");
+ v4->current->throwError(r->v8->toString(error));
+ }
+
+ QQmlContextData *context = r->v8->callingContext();
+
+ QQmlPropertyData cacheData;
+ cacheData.setFlags(QQmlPropertyData::IsWritable |
+ QQmlPropertyData::IsValueTypeVirtual);
+ cacheData.propType = reference->object->metaObject()->property(reference->property).userType();
+ cacheData.coreIndex = reference->property;
+ cacheData.valueTypeFlags = 0;
+ cacheData.valueTypeCoreIndex = index;
+ cacheData.valueTypePropType = p.userType();
+
+ QV4::ExecutionEngine::StackFrame frame = v4->currentStackFrame();
+
+ newBinding = new QQmlBinding(value, reference->object, context,
+ frame.source, qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column));
+ newBinding->setTarget(reference->object, cacheData, context);
+ newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
+ QQmlBinding::RequiresThisObject);
+ }
+
+ QQmlAbstractBinding *oldBinding =
+ QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding);
+ if (oldBinding)
+ oldBinding->destroy();
+
+ if (!f) {
+ QVariant v = r->v8->toVariant(value, -1);
+
+ if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
+ v = v.toInt();
+
+ p.write(reference->type, v);
+
+ if (writebackProperty.userType() == QMetaType::QVariant) {
+ QVariant variantReferenceValue = r->type->value();
+ reference->type->writeVariantValue(reference->object, reference->property, 0, &variantReferenceValue);
+ } else {
+ reference->type->write(reference->object, reference->property, 0);
+ }
+ }
+
+ } else {
+ Q_ASSERT(r->objectType == QmlValueTypeWrapper::Copy);
+
+ QmlValueTypeCopy *copy = static_cast<QmlValueTypeCopy *>(r);
+
+ int index = r->type->metaObject()->indexOfProperty(propName.constData());
+ if (index == -1)
+ return;
+
+ QVariant v = r->v8->toVariant(value, -1);
+
+ r->type->setValue(copy->value);
+ QMetaProperty p = r->type->metaObject()->property(index);
+ p.write(r->type, v);
+ copy->value = r->type->value();
+ }
+}
+
+QT_END_NAMESPACE