diff options
Diffstat (limited to 'src/qml/qml/qqmlglobal.cpp')
-rw-r--r-- | src/qml/qml/qqmlglobal.cpp | 854 |
1 files changed, 768 insertions, 86 deletions
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index d4473b7020..1273067187 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -1,69 +1,147 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qqmlglobal_p.h> -#include <QtQml/private/qqmlmetatype_p.h> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include <QtQml/private/qjsvalue_p.h> +#include <QtQml/private/qqmlglobal_p.h> +#include <QtQml/private/qqmlmetatype_p.h> #include <QtQml/qqmlengine.h> -#include <QtCore/qvariant.h> -#include <QtCore/qstringlist.h> + +#include <QtCore/private/qvariant_p.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> -#include <QtCore/QCoreApplication> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE -bool QQmlValueTypeProvider::initValueType(int type, QVariant& dst) +// Pre-filter the metatype before poking QQmlMetaType::qmlType() and locking its mutex. +static bool isConstructibleMetaType(const QMetaType metaType) { - const QMetaType metaType(type); - if (!metaType.isValid()) + switch (metaType.id()) { + // The builtins are not constructible this way. + case QMetaType::Void: + case QMetaType::Nullptr: + case QMetaType::QVariant: + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Float: + case QMetaType::Double: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::Char: + case QMetaType::SChar: + case QMetaType::UChar: + case QMetaType::QChar: + case QMetaType::QString: + case QMetaType::Bool: + case QMetaType::QDateTime: + case QMetaType::QDate: + case QMetaType::QTime: + case QMetaType::QUrl: + case QMetaType::QRegularExpression: + case QMetaType::QByteArray: + case QMetaType::QLocale: return false; - dst = QVariant(QMetaType(type)); + default: + break; + } + + // QJSValue is also builtin + if (metaType == QMetaType::fromType<QJSValue>()) + return false; + + // We also don't want to construct pointers of any kind, or lists, or enums. + if (metaType.flags() & + (QMetaType::PointerToQObject + | QMetaType::IsEnumeration + | QMetaType::SharedPointerToQObject + | QMetaType::WeakPointerToQObject + | QMetaType::TrackingPointerToQObject + | QMetaType::IsUnsignedEnumeration + | QMetaType::PointerToGadget + | QMetaType::IsPointer + | QMetaType::IsQmlList)) { + return false; + } + return true; } -bool QQmlValueTypeProvider::createValueType(int type, const QJSValue &s, QVariant &data) +static void *createVariantData(QMetaType type, QVariant *variant) { - const QQmlType qmlType = QQmlMetaType::qmlType(type, QQmlMetaType::TypeIdCategory::MetaType); - if (auto valueTypeFunction = qmlType.createValueTypeFunction()) { - QVariant result = valueTypeFunction(s); - if (result.userType() == type) { - data = std::move(result); + const QtPrivate::QMetaTypeInterface *iface = type.iface(); + QVariant::Private *d = &variant->data_ptr(); + Q_ASSERT(d->is_null && !d->is_shared); + *d = QVariant::Private(iface); + if (QVariant::Private::canUseInternalSpace(iface)) + return d->data.data; + + // This is not exception safe. + // If your value type throws an exception from its ctor bad things will happen anyway. + d->data.shared = QVariant::PrivateShared::create(iface->size, iface->alignment); + d->is_shared = true; + return d->data.shared->data(); +} + +static void callConstructor( + const QMetaObject *targetMetaObject, int i, void *source, void *target) +{ + void *p[] = { target, source }; + targetMetaObject->static_metacall(QMetaObject::ConstructInPlace, i, p); +} + +template<typename Allocate> +static void fromVerifiedType( + const QMetaObject *targetMetaObject, int ctorIndex, void *source, Allocate &&allocate) +{ + const QMetaMethod ctor = targetMetaObject->constructor(ctorIndex); + Q_ASSERT_X(ctor.parameterCount() == 1, "fromVerifiedType", + "Value type constructor must take exactly one argument"); + callConstructor(targetMetaObject, ctorIndex, source, allocate()); +} + + +template<typename Allocate, typename Retrieve> +static bool fromMatchingType( + const QMetaObject *targetMetaObject, Allocate &&allocate, Retrieve &&retrieve) +{ + for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) { + const QMetaMethod ctor = targetMetaObject->constructor(i); + if (ctor.parameterCount() != 1) + continue; + + const QMetaType parameterType = ctor.parameterMetaType(0); + + if (retrieve(parameterType, [&](QMetaType sourceMetaType, void *sourceData) { + if (sourceMetaType == parameterType) { + callConstructor(targetMetaObject, i, sourceData, allocate()); + return true; + } + + if (const QMetaObject *parameterMetaObject = parameterType.metaObject()) { + if (const QMetaObject *sourceMetaObject = sourceMetaType.metaObject(); + sourceMetaObject && sourceMetaObject->inherits(parameterMetaObject)) { + // Allow construction from derived types. + callConstructor(targetMetaObject, i, sourceData, allocate()); + return true; + } + } + + // Do not recursively try to create parameters here. This may end up in infinite recursion. + + // At this point, s should be a builtin type. For builtin types + // the QMetaType converters are good enough. + QVariant converted(parameterType); + if (QMetaType::convert(sourceMetaType, sourceData, parameterType, converted.data())) { + callConstructor(targetMetaObject, i, converted.data(), allocate()); + return true; + } + + return false; + })) { return true; } } @@ -71,40 +149,598 @@ bool QQmlValueTypeProvider::createValueType(int type, const QJSValue &s, QVarian return false; } -bool QQmlValueTypeProvider::equalValueType(int type, const void *lhs, const QVariant& rhs) +template<typename Allocate> +static bool fromMatchingType( + const QMetaObject *targetMetaObject, const QV4::Value &source, Allocate &&allocate) +{ + return fromMatchingType( + targetMetaObject, std::forward<Allocate>(allocate), + [&](QMetaType parameterType, auto callback) { + QVariant variant = QV4::ExecutionEngine::toVariant(source, parameterType); + return callback(variant.metaType(), variant.data()); + }); +} + +template<typename Allocate> +static bool fromMatchingType( + const QMetaObject *targetMetaObject, QVariant source, Allocate &&allocate) +{ + return fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate), + [&](QMetaType, auto callback) { + return callback(source.metaType(), source.data()); + }); +} + +template<typename Allocate> +static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate) +{ + for (int i = 0, end = mo->constructorCount(); i < end; ++i) { + const QMetaMethod ctor = mo->constructor(i); + if (ctor.parameterCount() != 1) + continue; + + if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) { + callConstructor(mo, i, &s, allocate()); + return true; + } + } + + return false; +} + +template<typename Get, typename Convert> +static bool doWriteProperty(const QMetaProperty &metaProperty, void *target, + Get &&get, Convert &&convert) +{ + const QMetaType propertyType = metaProperty.metaType(); + QVariant property = get(propertyType); + if (property.metaType() == propertyType) { + metaProperty.writeOnGadget(target, std::move(property)); + return true; + } + + QVariant converted = convert(propertyType); + if (converted.isValid()) { + metaProperty.writeOnGadget(target, std::move(converted)); + return true; + } + + converted = QVariant(propertyType); + if (QMetaType::convert(property.metaType(), property.constData(), + propertyType, converted.data())) { + metaProperty.writeOnGadget(target, std::move(converted)); + return true; + } + + return false; +} + +static void doWriteProperties( + const QMetaObject *targetMetaObject, void *target, const QV4::Value &source) +{ + const QV4::Object *o = static_cast<const QV4::Object *>(&source); + QV4::Scope scope(o->engine()); + QV4::ScopedObject object(scope, o); + + for (int i = 0; i < targetMetaObject->propertyCount(); ++i) { + const QMetaProperty metaProperty = targetMetaObject->property(i); + const QString propertyName = QString::fromUtf8(metaProperty.name()); + + QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName)); + QV4::ScopedValue v4PropValue(scope, object->get(v4PropName)); + + // We assume that data is freshly constructed. + // There is no point in reset()'ing properties of a freshly created object. + if (v4PropValue->isUndefined()) + continue; + + if (doWriteProperty(metaProperty, target, [&](const QMetaType &propertyType) { + return QV4::ExecutionEngine::toVariant(v4PropValue, propertyType); + }, [&](const QMetaType &propertyType) { + return QQmlValueTypeProvider::createValueType(v4PropValue, propertyType); + })) { + continue; + } + + const QMetaType propertyType = metaProperty.metaType(); + QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType); + if (property.metaType() == propertyType) { + metaProperty.writeOnGadget(target, std::move(property)); + continue; + } + + QVariant converted = QQmlValueTypeProvider::createValueType(v4PropValue, propertyType); + if (converted.isValid()) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + converted = QVariant(propertyType); + if (QMetaType::convert(property.metaType(), property.constData(), + propertyType, converted.data())) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + qWarning().noquote() + << QLatin1String("Could not convert %1 to %2 for property %3") + .arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()), + propertyName); + } +} + +static QVariant byProperties( + const QMetaObject *targetMetaObject, QMetaType metaType, const QV4::Value &source) { - Q_ASSERT(lhs); - return QMetaType(type).equals(lhs, rhs.constData()); + if (!source.isObject() || !targetMetaObject) + return QVariant(); + + QVariant result(metaType); + doWriteProperties(targetMetaObject, result.data(), source); + return result; } -bool QQmlValueTypeProvider::readValueType(const QVariant& src, void *dst, int type) +template<typename Read> +static void doWriteProperties( + const QMetaObject *targetMetaObject, void *target, + const QMetaObject *sourceMetaObject, Read &&read) +{ + for (int i = 0; i < targetMetaObject->propertyCount(); ++i) { + const QMetaProperty metaProperty = targetMetaObject->property(i); + + const int sourceProperty = sourceMetaObject->indexOfProperty(metaProperty.name()); + + // We assume that data is freshly constructed. + // There is no point in reset()'ing properties of a freshly created object. + if (sourceProperty == -1) + continue; + + const QMetaType propertyType = metaProperty.metaType(); + QVariant property = read(sourceMetaObject, sourceProperty); + if (property.metaType() == propertyType) { + metaProperty.writeOnGadget(target, std::move(property)); + continue; + } + + QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType); + if (converted.isValid()) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + converted = QVariant(propertyType); + if (QMetaType::convert(property.metaType(), property.constData(), + propertyType, converted.data())) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + qWarning().noquote() + << QLatin1String("Could not convert %1 to %2 for property %3") + .arg(property.toString(), QString::fromUtf8(propertyType.name()), + QString::fromUtf8(metaProperty.name())); + } +} + + +static void doWriteProperties(const QMetaObject *targetMeta, void *target, QObject *source) { - Q_ASSERT(dst); - const QMetaType dstType(type); - if (!dstType.isValid() || (src.metaType() == dstType && dstType.equals(src.constData(), dst))) + doWriteProperties( + targetMeta, target, source->metaObject(), + [source](const QMetaObject *sourceMetaObject, int sourceProperty) { + return sourceMetaObject->property(sourceProperty).read(source); + }); +} + +static QVariant byProperties( + const QMetaObject *targetMetaObject, QMetaType targetMetaType, QObject *source) +{ + if (!source || !targetMetaObject) + return QVariant(); + + QVariant result(targetMetaType); + doWriteProperties(targetMetaObject, result.data(), source); + return result; +} + +static QVariant byProperties( + const QMetaObject *targetMetaObject, QMetaType targetMetaType, + const QMetaObject *sourceMetaObject, const void *source) +{ + if (!source || !sourceMetaObject || !targetMetaObject) + return QVariant(); + + QVariant result(targetMetaType); + doWriteProperties( + targetMetaObject, result.data(), sourceMetaObject, + [source](const QMetaObject *sourceMetaObject, int sourceProperty) { + return sourceMetaObject->property(sourceProperty).readOnGadget(source); + }); + return result; +} + +template<typename Map> +void doWriteProperties(const QMetaObject *targetMetaObject, void *target, const Map &source) +{ + for (int i = 0; i < targetMetaObject->propertyCount(); ++i) { + const QMetaProperty metaProperty = targetMetaObject->property(i); + + // We assume that data is freshly constructed. + // There is no point in reset()'ing properties of a freshly created object. + const auto it = source.constFind(QString::fromUtf8(metaProperty.name())); + if (it == source.constEnd()) + continue; + + const QMetaType propertyType = metaProperty.metaType(); + QVariant property = *it; + if (property.metaType() == propertyType) { + metaProperty.writeOnGadget(target, std::move(property)); + continue; + } + + QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType); + if (converted.isValid()) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + converted = QVariant(propertyType); + if (QMetaType::convert(property.metaType(), property.constData(), + propertyType, converted.data())) { + metaProperty.writeOnGadget(target, std::move(converted)); + continue; + } + + qWarning().noquote() + << QLatin1String("Could not convert %1 to %2 for property %3") + .arg(property.toString(), QString::fromUtf8(propertyType.name()), + QString::fromUtf8(metaProperty.name())); + } +} + +template<typename Map> +QVariant byProperties( + const QMetaObject *targetMetaObject, QMetaType targetMetaType, const Map &source) +{ + QVariant result(targetMetaType); + doWriteProperties(targetMetaObject, result.data(), source); + return result; +} + +static QVariant byProperties( + const QMetaObject *targetMetaObject, QMetaType targetMetaType, const QVariant &source) +{ + if (!targetMetaObject) + return QVariant(); + + if (source.metaType() == QMetaType::fromType<QJSValue>()) { + QJSValue val = source.value<QJSValue>(); + return byProperties( + targetMetaObject, targetMetaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val))); + } + + if (source.metaType() == QMetaType::fromType<QVariantMap>()) { + return byProperties( + targetMetaObject, targetMetaType, + *static_cast<const QVariantMap *>(source.constData())); + } + + if (source.metaType() == QMetaType::fromType<QVariantHash>()) { + return byProperties( + targetMetaObject, targetMetaType, + *static_cast<const QVariantHash *>(source.constData())); + } + + if (source.metaType().flags() & QMetaType::PointerToQObject) + return byProperties(targetMetaObject, targetMetaType, source.value<QObject *>()); + + if (const QMetaObject *sourceMeta = QQmlMetaType::metaObjectForValueType(source.metaType())) + return byProperties(targetMetaObject, targetMetaType, sourceMeta, source.constData()); + + return QVariant(); +} + +template<typename Allocate, typename DefaultConstruct> +bool createOrConstructValueType( + const QQmlType &targetType, const QV4::Value &source, + Allocate &&allocate, DefaultConstruct &&defaultConstruct) +{ + const auto warn = [&](const QMetaObject *targetMetaObject) { + qWarning().noquote() + << "Could not find any constructor for value type" + << targetMetaObject->className() << "to call with value" + << source.toQStringNoThrow(); + }; + + if (targetType.canPopulateValueType()) { + if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) { + if (source.isObject()) { + doWriteProperties(targetMetaObject, defaultConstruct(), source); + return true; + } + if (targetType.canConstructValueType()) { + if (fromMatchingType(targetMetaObject, source, allocate)) + return true; + warn(targetMetaObject); + } + } + } else if (targetType.canConstructValueType()) { + if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) { + if (fromMatchingType(targetMetaObject, source, allocate)) + return true; + warn(targetMetaObject); + } + } + + if (const auto valueTypeFunction = targetType.createValueTypeFunction()) { + const QVariant result + = valueTypeFunction(QJSValuePrivate::fromReturnedValue(source.asReturnedValue())); + const QMetaType resultType = result.metaType(); + if (resultType == targetType.typeId()) { + resultType.construct(allocate(), result.constData()); + return true; + } + } + + return false; +} + +template<typename Allocate, typename DefaultConstruct> +bool createOrConstructValueType( + const QQmlType &targetType, QMetaType sourceMetaType, void *source, + Allocate &&allocate, DefaultConstruct &&defaultConstruct) +{ + + const auto warn = [&](const QMetaObject *targetMetaObject) { + qWarning().noquote() + << "Could not find any constructor for value type" + << targetMetaObject->className() << "to call with value" << source; + }; + + if (targetType.canPopulateValueType()) { + if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) { + if (const QMetaObject *sourceMetaObject + = QQmlMetaType::metaObjectForValueType(sourceMetaType)) { + doWriteProperties( + targetMetaObject, defaultConstruct(), sourceMetaObject, + [&source](const QMetaObject *sourceMetaObject, int sourceProperty) { + return sourceMetaObject->property(sourceProperty).readOnGadget(source); + }); + return true; + } + + if (sourceMetaType == QMetaType::fromType<QVariantMap>()) { + doWriteProperties( + targetMetaObject, defaultConstruct(), + *static_cast<const QVariantMap *>(source)); + return true; + } + + if (sourceMetaType == QMetaType::fromType<QVariantHash>()) { + doWriteProperties( + targetMetaObject, defaultConstruct(), + *static_cast<const QVariantHash *>(source)); + return true; + } + + if (sourceMetaType.flags() & QMetaType::PointerToQObject) { + doWriteProperties( + targetMetaObject, defaultConstruct(), + *static_cast<QObject *const *>(source)); + return true; + } + } + } + + if (targetType.canConstructValueType()) { + if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) { + if (fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate), + [&](QMetaType, auto callback) { + return callback(sourceMetaType, source); + })) { + return true; + } + warn(targetMetaObject); + } + } + + return false; +} + +/*! + * \internal + * Populate the value type in place at \a target, which is expected to be + * allocated and default-constructed, for example the result of a QVariant(QMetaType). + * This is efficient if we can do byProperties() since it can use the pre-constructed object. + * It also avoids the creation of a QVariant in most cases. It is not + * efficient if you're going to create a QVariant anyway. + */ +bool QQmlValueTypeProvider::populateValueType( + QMetaType targetMetaType, void *target, QMetaType sourceMetaType, void *source) +{ + if (sourceMetaType == QMetaType::fromType<QJSValue>()) { + const QJSValue *val = static_cast<const QJSValue *>(source); + return populateValueType( + targetMetaType, target, QV4::Value(QJSValuePrivate::asReturnedValue(val))); + } + + if (!isConstructibleMetaType(targetMetaType)) return false; - dstType.destruct(dst); - dstType.construct(dst, src.metaType() == dstType ? src.constData() : nullptr); - return true; + return createOrConstructValueType( + QQmlMetaType::qmlType(targetMetaType), sourceMetaType, source, + [targetMetaType, target]() { + targetMetaType.destruct(target); + return target; + }, [target]() { + return target; + }); } -bool QQmlValueTypeProvider::writeValueType(int type, const void *src, QVariant& dst) +/*! + * \internal + * Populate the value type in place at \a target, which is expected to be + * allocated and default-constructed, for example the result of a QVariant(QMetaType). + * This is efficient if we can do byProperties() since it can use the pre-constructed object. + * It also avoids the creation of a QVariant in most cases. It is not + * efficient if you're going to create a QVariant anyway. + */ +bool QQmlValueTypeProvider::populateValueType( + QMetaType targetMetaType, void *target, const QV4::Value &source) { - Q_ASSERT(src); - const QMetaType srcType(type); - if (!srcType.isValid() || (dst.metaType() == srcType && srcType.equals(src, dst.constData()))) + if (!isConstructibleMetaType(targetMetaType)) return false; - dst = QVariant(srcType, src); - return true; + return createOrConstructValueType( + QQmlMetaType::qmlType(targetMetaType), source, [targetMetaType, target]() { + targetMetaType.destruct(target); + return target; + }, [target]() { + return target; + }); +} + +/*! + * \internal + * Specialization that constructs the value type on the heap using new and returns a pointer to it. + */ +void *QQmlValueTypeProvider::heapCreateValueType( + const QQmlType &targetType, const QV4::Value &source) +{ + void *target = nullptr; + if (createOrConstructValueType( + targetType, source, [&]() { + const QMetaType metaType = targetType.typeId(); + const ushort align = metaType.alignOf(); + target = align > __STDCPP_DEFAULT_NEW_ALIGNMENT__ + ? operator new(metaType.sizeOf(), std::align_val_t(align)) + : operator new(metaType.sizeOf()); + return target; + }, [&]() { + target = targetType.typeId().create(); + return target; + })) { + Q_ASSERT(target != nullptr); + } + + return target; } -Q_GLOBAL_STATIC(QQmlValueTypeProvider, valueTypeProvider) +QVariant QQmlValueTypeProvider::constructValueType( + QMetaType targetMetaType, const QMetaObject *targetMetaObject, + int ctorIndex, void *ctorArg) +{ + QVariant result; + fromVerifiedType(targetMetaObject, ctorIndex, ctorArg, + [&]() { return createVariantData(targetMetaType, &result); }); + return result; +} -Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider() +static QVariant fromJSValue(const QQmlType &type, const QJSValue &s, QMetaType metaType) { - return valueTypeProvider(); + if (const auto valueTypeFunction = type.createValueTypeFunction()) { + const QVariant result = valueTypeFunction(s); + if (result.metaType() == metaType) + return result; + } + + return QVariant(); +} + +QVariant QQmlValueTypeProvider::createValueType(const QJSValue &s, QMetaType metaType) +{ + if (!isConstructibleMetaType(metaType)) + return QVariant(); + return fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType); +} + +QVariant QQmlValueTypeProvider::createValueType(const QString &s, QMetaType metaType) +{ + if (!isConstructibleMetaType(metaType)) + return QVariant(); + const QQmlType type = QQmlMetaType::qmlType(metaType); + if (type.canConstructValueType()) { + if (const QMetaObject *mo = type.metaObjectForValueType()) { + QVariant result; + if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + } + } + + return fromJSValue(type, s, metaType); +} + +QVariant QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType) +{ + if (!isConstructibleMetaType(metaType)) + return QVariant(); + const QQmlType type = QQmlMetaType::qmlType(metaType); + const auto warn = [&](const QMetaObject *mo) { + qWarning().noquote() + << "Could not find any constructor for value type" + << mo->className() << "to call with value" << s.toQStringNoThrow(); + }; + + if (type.canPopulateValueType()) { + if (const QMetaObject *mo = type.metaObject()) { + QVariant result = byProperties(mo, metaType, s); + if (result.isValid()) + return result; + if (type.canConstructValueType()) { + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(mo); + } + } + } else if (type.canConstructValueType()) { + if (const QMetaObject *mo = type.metaObject()) { + QVariant result; + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(mo); + } + } + + return fromJSValue(type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType); + +} + +/*! + * \internal + * This should only be called with either builtin types or wrapped QJSValues as source. + */ +QVariant QQmlValueTypeProvider::createValueType(const QVariant &s, QMetaType metaType) +{ + if (!isConstructibleMetaType(metaType)) + return QVariant(); + const QQmlType type = QQmlMetaType::qmlType(metaType); + const auto warn = [&](const QMetaObject *mo) { + qWarning().noquote() + << "Could not find any constructor for value type" + << mo->className() << "to call with value" << s; + }; + + if (type.canPopulateValueType()) { + if (const QMetaObject *mo = type.metaObjectForValueType()) { + QVariant result = byProperties(mo, metaType, s); + if (result.isValid()) + return result; + if (type.canConstructValueType()) { + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(mo); + } + } + } else if (type.canConstructValueType()) { + if (const QMetaObject *mo = type.metaObjectForValueType()) { + QVariant result; + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(mo); + } + } + + return QVariant(); } QQmlColorProvider::~QQmlColorProvider() {} @@ -123,7 +759,7 @@ QVariant QQmlColorProvider::tint(const QVariant &, const QVariant &) { return QV static QQmlColorProvider *colorProvider = nullptr; -Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider) +Q_QML_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider) { QQmlColorProvider *old = colorProvider; colorProvider = newProvider; @@ -177,7 +813,7 @@ QString QQmlGuiProvider::pluginName() const { return QString(); } static QQmlGuiProvider *guiProvider = nullptr; -Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider) +Q_QML_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider) { QQmlGuiProvider *old = guiProvider; guiProvider = newProvider; @@ -202,18 +838,8 @@ Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(void) //Docs in qqmlengine.cpp QQmlApplication::QQmlApplication(QObject *parent) - : QObject(*(new QQmlApplicationPrivate),parent) + : QQmlApplication(*(new QQmlApplicationPrivate), parent) { - connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), - this, SIGNAL(aboutToQuit())); - connect(QCoreApplication::instance(), SIGNAL(applicationNameChanged()), - this, SIGNAL(nameChanged())); - connect(QCoreApplication::instance(), SIGNAL(applicationVersionChanged()), - this, SIGNAL(versionChanged())); - connect(QCoreApplication::instance(), SIGNAL(organizationNameChanged()), - this, SIGNAL(organizationChanged())); - connect(QCoreApplication::instance(), SIGNAL(organizationDomainChanged()), - this, SIGNAL(domainChanged())); } QQmlApplication::QQmlApplication(QQmlApplicationPrivate &dd, QObject *parent) @@ -281,6 +907,62 @@ void QQmlApplication::setDomain(const QString &arg) QCoreApplication::instance()->setOrganizationDomain(arg); } +static const QQmlData *ddata_for_cast(QObject *object) +{ + Q_ASSERT(object); + auto ddata = QQmlData::get(object, false); + return (ddata && ddata->propertyCache) ? ddata : nullptr; +} + +bool qmlobject_can_cpp_cast(QObject *object, const QMetaObject *mo) +{ + Q_ASSERT(mo); + if (const QQmlData *ddata = ddata_for_cast(object)) + return ddata->propertyCache->firstCppMetaObject()->inherits(mo); + return object->metaObject()->inherits(mo); +} + +bool qmlobject_can_qml_cast(QObject *object, const QQmlType &type) +{ + Q_ASSERT(type.isValid()); + + // A non-composite type will always have a metaobject. + const QMetaObject *typeMetaObject = type.metaObject(); + const QQmlPropertyCache::ConstPtr typePropertyCache = typeMetaObject + ? QQmlPropertyCache::ConstPtr() + : QQmlMetaType::findPropertyCacheInCompositeTypes(type.typeId()); + + if (const QQmlData *ddata = ddata_for_cast(object)) { + for (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data(); propertyCache; + propertyCache = propertyCache->parent().data()) { + + if (typeMetaObject) { + // Prefer the metaobject inheritance mechanism, since it is more accurate. + // + // Assume the object can be casted to the type. Then, if we have a type metaobject, + // the object's property cache inheritance has to contain it. Otherwise we would + // end up with diverging metaobject hierarchies if we created the object's + // metaobject. This would be a disaster. + if (const QMetaObject *objectMetaObject = propertyCache->metaObject()) + return objectMetaObject->inherits(typeMetaObject); + } else { + // This is a best effort attempt. There are a number of ways for the + // property caches to be unrelated but the types still convertible. + // Multiple property caches can hold the same metaobject, for example for + // versions of non-composite types. + if (propertyCache == typePropertyCache.data()) + return true; + } + } + } + + // If nothing else works, we have to create the metaobjects. + + return object->metaObject()->inherits(typeMetaObject + ? typeMetaObject + : (typePropertyCache ? typePropertyCache->createMetaObject() : nullptr)); +} + QT_END_NAMESPACE #include "moc_qqmlglobal_p.cpp" |