From 0925c51c5988bee7ee96944de2d857b2132ebe65 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 15 Nov 2022 11:57:47 +0100 Subject: QJSEngine: Optimize conversion from QObject* to QString This is commonly done for logging. With this in place we can have the code generator use coerceType() for such constructs. [ChangeLog][QML][Important Behavior Changes] You can implement custom toString() methods for your QML objects in JavaScript or in C++. Those methods don't actually have to return a string. Previously, whatever return value the method generated was forwarded as-is. Now it is coerced to a string. Change-Id: I4a9721a6948be0c24a36b31d453a74bd747db729 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel --- src/qml/jsapi/qjsengine.cpp | 6 +++ src/qml/jsapi/qjsengine.h | 13 +++++++ src/qml/jsruntime/qv4qobjectwrapper.cpp | 65 ++++++++++++++++++--------------- src/qml/jsruntime/qv4qobjectwrapper_p.h | 3 ++ src/qmlcompiler/qqmljscodegenerator.cpp | 5 +++ 5 files changed, 63 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index c15b5c1ccd..8b0b650c1f 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -920,6 +920,12 @@ bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromData(fromType, from), toType, to); } +QString QJSEngine::convertQObjectToString(QObject *object) +{ + return QV4::QObjectWrapper::objectToString( + handle(), object ? object->metaObject() : nullptr, object); +} + /*! \fn template QJSValue QJSEngine::toScriptValue(const T &value) Creates a QJSValue with the given \a value. diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index db78e1b9a9..9af6fd6e7c 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -101,6 +101,13 @@ public: if constexpr (std::is_same_v) return toManagedValue(value); + if constexpr (std::is_same_v) { + if (targetType.flags() & QMetaType::PointerToQObject) { + return convertQObjectToString( + *reinterpret_cast(value.constData())); + } + } + if (sourceType == QMetaType::fromType()) return fromScriptValue(*reinterpret_cast(value.constData())); @@ -148,6 +155,11 @@ public: if constexpr (std::is_same_v) return QVariant::fromValue(from); + if constexpr (std::is_same_v + && std::is_base_of_v>>) { + return convertQObjectToString(from); + } + if constexpr (std::is_same_v> const *>) { using nonConstTo = std::remove_const_t> *; if constexpr (std::is_same_v) @@ -220,6 +232,7 @@ private: bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr); bool convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to); + QString convertQObjectToString(QObject *object); template friend inline T qjsvalue_cast(const QJSValue &); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index e83c111251..2d33b5f36e 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1023,6 +1023,38 @@ int QObjectWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int return 0; } +QString QObjectWrapper::objectToString( + ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object) +{ + if (!metaObject) + return QLatin1String("null"); + + if (!object) + return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)"); + + const int id = metaObject->indexOfMethod("toString()"); + if (id >= 0) { + const QMetaMethod method = metaObject->method(id); + const QMetaType returnType = method.returnMetaType(); + QVariant result(returnType); + method.invoke(object, QGenericReturnArgument(returnType.name(), result.data())); + if (result.metaType() == QMetaType::fromType()) + return result.toString(); + QV4::Scope scope(engine); + QV4::ScopedValue value(scope, engine->fromVariant(result)); + return value->toQString(); + } + + QString result; + result += QString::fromUtf8(metaObject->className()) + + QLatin1String("(0x") + QString::number(quintptr(object), 16); + QString objectName = object->objectName(); + if (!objectName.isEmpty()) + result += QLatin1String(", \"") + objectName + QLatin1Char('\"'); + result += QLatin1Char(')'); + return result; +} + struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase { PersistentValue function; @@ -2385,12 +2417,9 @@ static QObject *qObject(const Value *v) return nullptr; } -ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, const QV4::Value *thisObject) const +ReturnedValue QObjectMethod::method_toString( + ExecutionEngine *engine, const QV4::Value *thisObject) const { - const auto encode = [engine](const QString &result) { - return engine->newString(result)->asReturnedValue(); - }; - QObject *o = qObject(thisObject); d()->assertIntegrity(o); @@ -2398,31 +2427,9 @@ ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, const QV4: const QMetaObject *metaObject = d()->metaObject(); if (!metaObject && o) metaObject = o->metaObject(); - if (metaObject) { - if (QObject *qobject = o ? o : d()->object()) { - const int id = metaObject->indexOfMethod("toString()"); - if (id >= 0) { - const QMetaMethod method = metaObject->method(id); - const QMetaType returnType = method.returnMetaType(); - QVariant result(returnType); - method.invoke(qobject, QGenericReturnArgument(returnType.name(), result.data())); - return engine->fromVariant(result); - } - - QString result; - result += QString::fromUtf8(metaObject->className()) + - QLatin1String("(0x") + QString::number(quintptr(qobject), 16); - QString objectName = qobject->objectName(); - if (!objectName.isEmpty()) - result += QLatin1String(", \"") + objectName + QLatin1Char('\"'); - result += QLatin1Char(')'); - return encode(result); - } else { - return encode(QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)")); - } - } - return encode(QLatin1String("null")); + return engine->newString(QObjectWrapper::objectToString( + engine, metaObject, o ? o : d()->object()))->asReturnedValue(); } ReturnedValue QObjectMethod::method_destroy(ExecutionEngine *engine, const QV4::Value *thisObject, const Value *args, int argc) const diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 043afe79e7..d17b12ea4a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -194,6 +194,9 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a); + static QString objectToString( + ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object); + protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue create(ExecutionEngine *engine, QObject *object); diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index e360644b08..adbe591045 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -2898,6 +2898,11 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, return retrieve; } + if (from->isReferenceType() && m_typeResolver->equals(to, m_typeResolver->stringType())) { + return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", " + + castTargetName(to) + u">("_s + variable + u')'; + } + // TODO: add more conversions reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName()); -- cgit v1.2.3