diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-02-14 15:48:56 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2019-02-17 08:10:17 +0000 |
commit | 94d30df911dccd830a18d7c2e75397505ef9a600 (patch) | |
tree | e9890f9cb006fdc38bbed9fe94d553868d905166 | |
parent | d96a700cc3611480ff76023287cb06f455a37b02 (diff) |
Check parameter types when invoking C++ functions from QML
We now check if the given parameters can be converted to the expected
arguments of the function being invoked and throw a type error if not.
Previously we would still invoke the method with random parameters.
[ChangeLog][QtQml][Important Behavior Changes] The parameters passed to
C++ functions from QML are now checked for compatibility with the
expected arguments. If they cannot be converted, a type error is thrown
in JavaScript and the function is not invoked.
Fixes: QTBUG-73405
Change-Id: If16089510d314bb7cdb7d4db86478114c61281a8
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 40 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 42 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/SignalEmitter.qml | 14 |
3 files changed, 61 insertions, 35 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 9344a231ff..6617dd5e89 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1144,7 +1144,7 @@ struct CallArgument { inline void *dataPtr(); inline void initAsType(int type); - inline void fromValue(int type, ExecutionEngine *, const QV4::Value &); + inline bool fromValue(int type, ExecutionEngine *, const QV4::Value &); inline ReturnedValue toValue(ExecutionEngine *); private: @@ -1204,8 +1204,12 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index // Convert all arguments. QVarLengthArray<CallArgument, 9> args(argCount + 1); args[0].initAsType(returnType); - for (int ii = 0; ii < argCount; ++ii) - args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii]); + for (int ii = 0; ii < argCount; ++ii) { + if (!args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii])) { + return engine->throwTypeError(QString::fromLatin1("Could not convert argument %1.") + .arg(ii)); + } + } QVarLengthArray<void *, 9> argData(args.count()); for (int ii = 0; ii < args.count(); ++ii) argData[ii] = args[ii].dataPtr(); @@ -1672,7 +1676,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M } #endif -void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) +bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { if (type != 0) { cleanup(); @@ -1708,11 +1712,13 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::QObjectStar) { qobjectPtr = nullptr; + type = callType; if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) qobjectPtr = qobjectWrapper->object(); else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>()) queryEngine = qmlTypeWrapper->isSingleton(); - type = callType; + else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr + return false; } else if (callType == qMetaTypeId<QVariant>()) { qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, -1)); type = callType; @@ -1734,6 +1740,8 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q QObject *o = nullptr; if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) o = qobjectWrapper->object(); + else if (!value.isNull() && !value.isUndefined()) + return false; qlistPtr->append(o); } type = callType; @@ -1782,6 +1790,15 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); } #endif + } else if (QMetaType::typeFlags(callType) + & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) { + // You can assign null or undefined to any pointer. The result is a nullptr. + if (value.isNull() || value.isUndefined()) { + qvariantPtr = new (&allocData) QVariant(callType, nullptr); + type = callType; + } else { + queryEngine = true; + } } else { queryEngine = true; } @@ -1803,15 +1820,20 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q if (!mo.isNull()) { QObject *obj = ep->toQObject(v); - if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) - obj = nullptr; + if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) { + *qvariantPtr = QVariant(callType, nullptr); + return false; + } *qvariantPtr = QVariant(callType, &obj); - } else { - *qvariantPtr = QVariant(callType, (void *)nullptr); + return true; } + + *qvariantPtr = QVariant(callType, (void *)nullptr); + return false; } } + return true; } QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 788ecce1c5..6a97fc5d8b 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -2762,32 +2762,28 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->actuals().at(0), QVariant(QString())); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QPointF(0)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 12); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), QVariant(QPointF())); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QPointF(null)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 12); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), QVariant(QPointF())); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QPointF(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 12); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), QVariant(QPointF())); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QPointF(object)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 12); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), QVariant(QPointF())); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue())); @@ -2804,18 +2800,16 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->actuals().at(0), QVariant(QPointF(9, 12))); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QObject(0)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 13); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); - QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue())); + QVERIFY(!EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), 13); - QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->invoked(), -1); + QCOMPARE(o->actuals().count(), 0); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue())); diff --git a/tests/auto/qml/qqmllanguage/data/SignalEmitter.qml b/tests/auto/qml/qqmllanguage/data/SignalEmitter.qml index 259f45b7d2..31fe5e4a5e 100644 --- a/tests/auto/qml/qqmllanguage/data/SignalEmitter.qml +++ b/tests/auto/qml/qqmllanguage/data/SignalEmitter.qml @@ -10,8 +10,18 @@ QtObject { signal testSignal(SignalParam spp); function emitTestSignal() { - testObject.expectNull = true; - testSignal(op); + var caught = false; + try { + testObject.expectNull = true; + testSignal(op); + } catch(e) { + // good: We want a type error here + caught = true; + if (handleSignal) + testObject.determineSuccess(null); + } + if (!caught && handleSignal) + testObject.determineSuccess("fail"); testObject.expectNull = false; testSignal(p); |