diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2022-11-15 10:13:09 +0100 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-11-15 14:26:17 +0100 |
| commit | 7b29ed6f64a300d4d714371c2872fcba344beebd (patch) | |
| tree | d2e83316030d03a05317958e22770bb784ffcbe0 /src | |
| parent | d9d247746423f6a9f1f36aa9be14fad1edcd6616 (diff) | |
QJSEngine: Provide a method to coerce values in JS fashion
JavaScript has its own type coercion rules. We already have a methods
that coerce QVariants, QJSValues and QJSManagedValues to specific types.
The new method is a generalization of all of those and can coerce
everything to everything (as far as JavaScript can).
Change-Id: I9b6877fb40f67b6f2354781bbd4cf18cf996c7b0
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 31 | ||||
| -rw-r--r-- | src/qml/jsapi/qjsengine.h | 73 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 7 |
3 files changed, 99 insertions, 12 deletions
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 61042607c9..98df83fd99 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -833,7 +833,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) return convertV2(value, QMetaType(type), ptr); } -static bool convertString(const QString &string, QMetaType metaType, void *ptr) +bool QJSEngine::convertString(const QString &string, QMetaType metaType, void *ptr) { // have a string based value without engine. Do conversion manually if (metaType == QMetaType::fromType<bool>()) { @@ -906,20 +906,24 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr) bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr) { - if (value.metaType() == QMetaType::fromType<QString>()) - return convertString(value.toString(), metaType, ptr); - // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here. return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromVariant(value), metaType, ptr); } +bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to + // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here. + return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromData(fromType, from), toType, to); +} + /*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value) Creates a QJSValue with the given \a value. This works with any type \c{T} that has a \c{QMetaType}. - \sa fromScriptValue() + \sa fromScriptValue(), coerceValue() */ /*! \fn template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value) @@ -927,7 +931,7 @@ bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void * Returns the given \a value converted to the template type \c{T}. This works with any type \c{T} that has a \c{QMetaType}. - \sa toScriptValue() + \sa toScriptValue(), coerceValue() */ /*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value) @@ -939,7 +943,20 @@ bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void * conversions between JavaScript-equivalent types that are not performed by qvariant_cast by default. - \sa fromScriptValue() qvariant_cast() + \sa coerceValue(), fromScriptValue(), qvariant_cast() +*/ + +/*! \fn template <typename From, typename To> T QJSEngine::coerceValue(const From &from) + + Returns the given \a from converted to the template type \c{To}. + This works with any type \c{T} that has a \c{QMetaType}. The + conversion is done in JavaScript semantics. Those differ from + qvariant_cast's semantics. There are a number of implicit + conversions between JavaScript-equivalent types that are not + performed by qvariant_cast by default. This method is a generalization of + all the other conversion methods in this class. + + \sa fromVariant(), qvariant_cast(), fromScriptValue(), toScriptValue() */ /*! diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 74f8b2892f..1dde093a01 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -83,8 +83,9 @@ public: if constexpr (std::is_same_v<T, QVariant>) return value; + const QMetaType sourceType = value.metaType(); const QMetaType targetType = QMetaType::fromType<T>(); - if (value.metaType() == targetType) + if (sourceType == targetType) return *reinterpret_cast<const T *>(value.constData()); if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) { @@ -94,16 +95,81 @@ public: return *reinterpret_cast<const nonConstT *>(value.constData()); } + if constexpr (std::is_same_v<T, QJSValue>) + return toScriptValue(value); + + if constexpr (std::is_same_v<T, QJSManagedValue>) + return toManagedValue(value); + + if (sourceType == QMetaType::fromType<QJSValue>()) + return fromScriptValue<T>(*reinterpret_cast<const QJSValue *>(value.constData())); + + if (sourceType == QMetaType::fromType<QJSManagedValue>()) { + return fromManagedValue<T>( + *reinterpret_cast<const QJSManagedValue *>(value.constData())); + } + + { T t{}; - if (convertVariant(value, targetType, &t)) + if (value.metaType() == QMetaType::fromType<QString>()) { + if (convertString(value.toString(), targetType, &t)) + return t; + } else if (convertVariant(value, targetType, &t)) { return t; + } QMetaType::convert(value.metaType(), value.constData(), targetType, &t); return t; } } + template<typename From, typename To> + inline To coerceValue(const From &from) + { + if constexpr (std::is_same_v<From, To>) + return from; + + if constexpr (std::is_same_v<To, QJSValue>) + return toScriptValue(from); + + if constexpr (std::is_same_v<From, QJSValue>) + return fromScriptValue<To>(from); + + if constexpr (std::is_same_v<To, QJSManagedValue>) + return toManagedValue(from); + + if constexpr (std::is_same_v<From, QJSManagedValue>) + return fromManagedValue<To>(from); + + if constexpr (std::is_same_v<From, QVariant>) + return fromVariant<To>(from); + + if constexpr (std::is_same_v<To, QVariant>) + return QVariant::fromValue(from); + + if constexpr (std::is_same_v<To, std::remove_const_t<std::remove_pointer_t<To>> const *>) { + using nonConstTo = std::remove_const_t<std::remove_pointer_t<To>> *; + if constexpr (std::is_same_v<From, nonConstTo>) + return from; + } + + { + const QMetaType sourceType = QMetaType::fromType<From>(); + const QMetaType targetType = QMetaType::fromType<To>(); + To to{}; + if constexpr (std::is_same_v<From, QString>) { + if (convertString(from, targetType, &to)) + return to; + } else if (convertMetaType(sourceType, &from, targetType, &to)) { + return to; + } + + QMetaType::convert(sourceType, &from, targetType, &to); + return to; + } + } + void collectGarbage(); enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; @@ -148,7 +214,10 @@ private: static bool convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr); static bool convertV2(const QJSValue &value, int type, void *ptr); static bool convertV2(const QJSValue &value, QMetaType metaType, void *ptr); + static bool convertString(const QString &string, QMetaType metaType, void *ptr); + bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr); + bool convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to); template<typename T> friend inline T qjsvalue_cast(const QJSValue &); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 067ddc7243..5434f55548 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -740,13 +740,14 @@ public: QV4::ReturnedValue callInContext(QV4::Function *function, QObject *self, QV4::ExecutionContext *ctxt, int argc, const QV4::Value *argv); + QV4::ReturnedValue fromData( + QMetaType type, const void *ptr, + Heap::Object *parent = nullptr, int property = -1, uint flags = 0); + private: template<int Frames> friend struct ExecutionEngineCallDepthRecorder; - QV4::ReturnedValue fromData( - QMetaType type, const void *ptr, - Heap::Object *parent = nullptr, int property = -1, uint flags = 0); static void initializeStaticMembers(); static int s_maxCallDepth; |
