QML: Allow conversion of symbols to QVariant

We produce their descriptiveString or simply a QVariant containing a
QJSValue, depending on whether we're supposed to convert objects without
equivalent C++ type or not.

Pick-to: 6.6 6.5 6.2
Fixes: QTBUG-113854
Change-Id: I22b6038c936d860fdd8aa227f9dfe704e3265a77
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2023-06-16 15:03:33 +02:00
parent e50b2069f9
commit 8c451bba7a
4 changed files with 40 additions and 12 deletions

View File

@ -163,10 +163,11 @@
/*!
\enum QJSValue::ObjectConversionBehavior
This enum is used to specify how JavaScript objects without an equivalent
This enum is used to specify how JavaScript objects and symbols without an equivalent
native Qt type should be treated when converting to QVariant.
\value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted.
Symbols are converted to QString.
\value RetainJSObjects The value is retained as QJSValue wrapped in QVariant.
*/

View File

@ -1478,7 +1478,9 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
// Variant conversion code
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
static QVariant toVariant(
const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols,
V4ObjectSet *visitedObjects);
static QObject *qtObjectFromJS(const QV4::Value &value);
static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
@ -1491,7 +1493,7 @@ static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &
}
static QVariant toVariant(
const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects,
const QV4::Value &value, QMetaType metaType, bool createJSValueForObjectsAndSymbols,
V4ObjectSet *visitedObjects)
{
Q_ASSERT (!value.isEmpty());
@ -1634,6 +1636,9 @@ static QVariant toVariant(
return *ld->d()->locale;
#endif
if (const QV4::DateObject *d = value.as<DateObject>()) {
// NOTE: since we convert QTime to JS Date,
// round trip will change the variant type (to QDateTime)!
if (metaType == QMetaType::fromType<QDate>())
return DateObject::dateTimeToDate(d->toQDateTime());
@ -1649,7 +1654,11 @@ static QVariant toVariant(
return d->toQUrl();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
// NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
if (const Symbol *symbol = value.as<Symbol>()) {
return createJSValueForObjectsAndSymbols
? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
: symbol->descriptiveString();
}
const QV4::Object *object = value.as<QV4::Object>();
Q_ASSERT(object);
@ -1667,16 +1676,17 @@ static QVariant toVariant(
return result;
}
if (createJSValueForObjects)
if (createJSValueForObjectsAndSymbols)
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
return objectToVariant(o, visitedObjects);
}
QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects)
QVariant ExecutionEngine::toVariant(
const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
{
return ::toVariant(value, typeHint, createJSValueForObjects, nullptr);
return ::toVariant(value, typeHint, createJSValueForObjectsAndSymbols, nullptr);
}
static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects)
@ -1707,7 +1717,8 @@ static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObject
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->get(ii);
list << ::toVariant(v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects);
list << ::toVariant(v, QMetaType {}, /*createJSValueForObjectsAndSymbols*/false,
visitedObjects);
}
result = list;
@ -1725,7 +1736,7 @@ static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObject
QString key = name->toQStringNoThrow();
map.insert(key, ::toVariant(
val, /*type hint*/ QMetaType {},
/*createJSValueForObjects*/false, visitedObjects));
/*createJSValueForObjectsAndSymbols*/false, visitedObjects));
}
result = map;
@ -2589,7 +2600,8 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
*reinterpret_cast<QVariantList *>(data) = ExecutionEngine::toVariant(
*a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList();
*a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false)
.toList();
return true;
}
break;
@ -2605,7 +2617,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
case QMetaType::QVariant:
if (value.as<QV4::Managed>()) {
*reinterpret_cast<QVariant*>(data) = ExecutionEngine::toVariant(
value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false);
value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
} else if (value.isNull()) {
*reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
} else if (value.isUndefined()) {

View File

@ -663,7 +663,7 @@ public:
// variant conversions
static QVariant toVariant(
const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects = true);
const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols = true);
QV4::ReturnedValue fromVariant(const QVariant &);
QV4::ReturnedValue fromVariant(
const QVariant &variant, Heap::Object *parent, int property, uint flags);

View File

@ -304,6 +304,8 @@ private slots:
void callWithSpreadOnElement();
void spreadNoOverflow();
void symbolToVariant();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@ -6083,6 +6085,19 @@ void tst_QJSEngine::spreadNoOverflow()
QCOMPARE(result.errorType(), QJSValue::RangeError);
}
void tst_QJSEngine::symbolToVariant()
{
QJSEngine engine;
const QJSValue val = engine.newSymbol("asymbol");
QCOMPARE(val.toVariant(), QStringLiteral("Symbol(asymbol)"));
const QVariant retained = val.toVariant(QJSValue::RetainJSObjects);
QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
QVERIFY(retained.value<QJSValue>().strictlyEquals(val));
QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)"));
}
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"