QmlCompiler: Perform QVariant conversion in JavaScript semantics
In JavaScript we have a number of extra conversions not covered by qvariant_cast. Therefore, add a method to perform a QVariant conversion in JavaScript semantics to QJSEngine, and use that in the compiler. Pick-to: 6.3 Fixes: QTBUG-100883 Change-Id: I8b0bfa0974bc6b339d2601fb373859bc710788c8 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Jarkko Koivikko <jarkko.koivikko@code-q.fi>
This commit is contained in:
parent
bd968fa6ff
commit
d0f4e0c037
|
@ -861,73 +861,87 @@ 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)
|
||||
{
|
||||
// have a string based value without engine. Do conversion manually
|
||||
if (metaType == QMetaType::fromType<bool>()) {
|
||||
*reinterpret_cast<bool*>(ptr) = string.length() != 0;
|
||||
return true;
|
||||
}
|
||||
if (metaType == QMetaType::fromType<QString>()) {
|
||||
*reinterpret_cast<QString*>(ptr) = string;
|
||||
return true;
|
||||
}
|
||||
if (metaType == QMetaType::fromType<QUrl>()) {
|
||||
*reinterpret_cast<QUrl *>(ptr) = QUrl(string);
|
||||
return true;
|
||||
}
|
||||
|
||||
double d = QV4::RuntimeHelpers::stringToNumber(string);
|
||||
switch (metaType.id()) {
|
||||
case QMetaType::Int:
|
||||
*reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UInt:
|
||||
*reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::LongLong:
|
||||
*reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
|
||||
return true;
|
||||
case QMetaType::ULongLong:
|
||||
*reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
|
||||
return true;
|
||||
case QMetaType::Double:
|
||||
*reinterpret_cast<double*>(ptr) = d;
|
||||
return true;
|
||||
case QMetaType::Float:
|
||||
*reinterpret_cast<float*>(ptr) = d;
|
||||
return true;
|
||||
case QMetaType::Short:
|
||||
*reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UShort:
|
||||
*reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::Char:
|
||||
*reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UChar:
|
||||
*reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::QChar:
|
||||
*reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
|
||||
return true;
|
||||
case QMetaType::Char16:
|
||||
*reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
convert \a value to \a type, store the result in \a ptr
|
||||
*/
|
||||
bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
|
||||
{
|
||||
if (const QString *string = QJSValuePrivate::asQString(&value)) {
|
||||
// have a string based value without engine. Do conversion manually
|
||||
if (metaType == QMetaType::fromType<bool>()) {
|
||||
*reinterpret_cast<bool*>(ptr) = string->length() != 0;
|
||||
return true;
|
||||
}
|
||||
if (metaType == QMetaType::fromType<QString>()) {
|
||||
*reinterpret_cast<QString*>(ptr) = *string;
|
||||
return true;
|
||||
}
|
||||
if (metaType == QMetaType::fromType<QUrl>()) {
|
||||
*reinterpret_cast<QUrl *>(ptr) = QUrl(*string);
|
||||
return true;
|
||||
}
|
||||
|
||||
double d = QV4::RuntimeHelpers::stringToNumber(*string);
|
||||
switch (metaType.id()) {
|
||||
case QMetaType::Int:
|
||||
*reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UInt:
|
||||
*reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::LongLong:
|
||||
*reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
|
||||
return true;
|
||||
case QMetaType::ULongLong:
|
||||
*reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
|
||||
return true;
|
||||
case QMetaType::Double:
|
||||
*reinterpret_cast<double*>(ptr) = d;
|
||||
return true;
|
||||
case QMetaType::Float:
|
||||
*reinterpret_cast<float*>(ptr) = d;
|
||||
return true;
|
||||
case QMetaType::Short:
|
||||
*reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UShort:
|
||||
*reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::Char:
|
||||
*reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
|
||||
return true;
|
||||
case QMetaType::UChar:
|
||||
*reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
case QMetaType::QChar:
|
||||
*reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
|
||||
return true;
|
||||
case QMetaType::Char16:
|
||||
*reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (const QString *string = QJSValuePrivate::asQString(&value))
|
||||
return convertString(*string, metaType, ptr);
|
||||
|
||||
return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, 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);
|
||||
}
|
||||
|
||||
/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
|
||||
|
||||
Creates a QJSValue with the given \a value.
|
||||
|
@ -944,6 +958,18 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
|
|||
\sa toScriptValue()
|
||||
*/
|
||||
|
||||
/*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value)
|
||||
|
||||
Returns the given \a value converted to the template type \c{T}.
|
||||
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.
|
||||
|
||||
\sa fromScriptValue() qvariant_cast()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Throws a run-time error (exception) with the given \a message.
|
||||
|
||||
|
|
|
@ -113,6 +113,33 @@ public:
|
|||
return qjsvalue_cast<T>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T fromVariant(const QVariant &value)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, QVariant>)
|
||||
return value;
|
||||
|
||||
const QMetaType targetType = QMetaType::fromType<T>();
|
||||
if (value.metaType() == 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 *>) {
|
||||
using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
|
||||
const QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
|
||||
if (value.metaType() == nonConstTargetType)
|
||||
return *reinterpret_cast<const nonConstT *>(value.constData());
|
||||
}
|
||||
|
||||
{
|
||||
T t{};
|
||||
if (convertVariant(value, QMetaType::fromType<T>(), &t))
|
||||
return t;
|
||||
|
||||
QMetaType::convert(value.metaType(), value.constData(), targetType, &t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
void collectGarbage();
|
||||
|
||||
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
|
||||
|
@ -157,6 +184,7 @@ 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);
|
||||
bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr);
|
||||
|
||||
template<typename T>
|
||||
friend inline T qjsvalue_cast(const QJSValue &);
|
||||
|
|
|
@ -2626,7 +2626,8 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
|
|||
if (m_typeResolver->equals(from, varType)) {
|
||||
if (m_typeResolver->equals(to, m_typeResolver->listPropertyType()))
|
||||
return u"QQmlListReference("_qs + variable + u", aotContext->qmlEngine())"_qs;
|
||||
return u"qvariant_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
|
||||
return u"aotContext->engine->fromVariant<"_qs + castTargetName(to) + u">("_qs
|
||||
+ variable + u')';
|
||||
}
|
||||
|
||||
if (m_typeResolver->equals(to, varType))
|
||||
|
|
|
@ -91,6 +91,7 @@ set(qml_files
|
|||
noscope.qml
|
||||
notEqualsInt.qml
|
||||
nullAccess.qml
|
||||
objectInVar.qml
|
||||
outOfBounds.qml
|
||||
overriddenMember.qml
|
||||
ownProperty.qml
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
pragma Strict
|
||||
import QtQml
|
||||
|
||||
QtObject {
|
||||
id: self
|
||||
|
||||
property var thing: self
|
||||
|
||||
function doThing() : bool {
|
||||
if (self.thing)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -120,6 +120,7 @@ private slots:
|
|||
void infinities();
|
||||
void blockComments();
|
||||
void functionLookup();
|
||||
void objectInVar();
|
||||
};
|
||||
|
||||
void tst_QmlCppCodegen::simpleBinding()
|
||||
|
@ -1804,6 +1805,24 @@ void tst_QmlCppCodegen::functionLookup()
|
|||
QCOMPARE(result.toString(), QStringLiteral("a99"));
|
||||
}
|
||||
|
||||
void tst_QmlCppCodegen::objectInVar()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/objectInVar.qml"_qs));
|
||||
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
|
||||
QScopedPointer<QObject> o(c.create());
|
||||
QVERIFY(o);
|
||||
QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
|
||||
|
||||
bool result = false;
|
||||
QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
|
||||
QVERIFY(result);
|
||||
|
||||
o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
|
||||
QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
|
||||
QVERIFY(!result);
|
||||
}
|
||||
|
||||
void tst_QmlCppCodegen::runInterpreted()
|
||||
{
|
||||
if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
|
||||
|
|
Loading…
Reference in New Issue