QmlCompiler: Reject conversions via QJSValue

Those are generally less efficient than what the interpreter would do,
they can have side effects, and they can throw exceptions. We don't want
to deal with any of that. Most of those implicit conversions have
explicit equivalents. For those that don't we can add them.

Pick-to: 6.2 6.4
Fixes: QTBUG-104010
Change-Id: I62898db92219386c94f2a6c9b56f6fb0b7578832
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Ulf Hermann 2022-06-03 15:12:10 +02:00
parent 48ae7468af
commit fb3a81623a
5 changed files with 39 additions and 7 deletions

View File

@ -2060,8 +2060,8 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
const auto rhsVar = conversion(
m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
m_state.accumulatorVariableIn);
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s;
@ -2262,8 +2262,8 @@ void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cp
registerVariable(lhs));
const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
m_state.accumulatorVariableIn);
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut;
@ -2611,10 +2611,10 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
return u"QJSPrimitiveValue("_s + variable + u')' + retrieve;
}
// TODO: more efficient string conversions, possibly others
// TODO: add more conversions
return u"aotContext->engine->fromScriptValue<"_s + castTargetName(to)
+ u">(aotContext->engine->toScriptValue("_s + variable + u"))"_s;
reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
return QString();
}
int QQmlJSCodeGenerator::nextJSLine(uint line) const

View File

@ -133,6 +133,7 @@ set(qml_files
text.qml
themerbad.qml
themergood.qml
toString.qml
typePropertyClash.qml
typedArray.qml
undefinedResets.qml

View File

@ -3,6 +3,7 @@ import TestTypes
import Ambiguous 1.2
QtObject {
id: self
property string attachedForNonObject: objectName.Component.objectName
property string attachedForNasty: Nasty.objectName
@ -27,4 +28,6 @@ QtObject {
}
Component.onCompleted: doesNotExist()
property string aString: self + "a"
}

View File

@ -0,0 +1,13 @@
import QtQml
QtObject {
id: self
property QtObject other: QtObject {
function toString() : string { throw "no" }
}
function toString() : string { return "yes" }
property string yes: self + " yes"
property string no: other + " no"
}

View File

@ -120,6 +120,7 @@ private slots:
void boundComponents();
void invisibleListElementType();
void typePropertyClash();
void objectToString();
};
void tst_QmlCppCodegen::simpleBinding()
@ -2194,6 +2195,20 @@ void tst_QmlCppCodegen::typePropertyClash()
QCOMPARE(o->objectName(), u"Size: 5"_s);
}
void tst_QmlCppCodegen::objectToString()
{
QQmlEngine engine;
QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/toString.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/toString.qml:6: no");
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
QCOMPARE(o->property("yes").toString(), u"yes yes"_s);
QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored
}
void tst_QmlCppCodegen::runInterpreted()
{
#ifdef Q_OS_ANDROID