From 2e4561d4e71f33a5c551a6d3509f453c62bf3c63 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 25 Feb 2021 12:24:10 +0100 Subject: [PATCH] QJSManagedValue: Throw TypeErrors when accessing null or undefined Accessing properties of null or undefined is supposed to throw TypeErrors in ECMAScript. Change-Id: I4f05d56fa2f4d6767e582795bf39769a12df8019 Reviewed-by: Fabian Kosmale --- src/qml/jsapi/qjsmanagedvalue.cpp | 13 ++++++- .../qjsmanagedvalue/tst_qjsmanagedvalue.cpp | 35 +++++++++++++++++-- .../qml/qjsmanagedvalue/tst_qjsmanagedvalue.h | 2 ++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp index 91fb930ba3..202c940785 100644 --- a/src/qml/jsapi/qjsmanagedvalue.cpp +++ b/src/qml/jsapi/qjsmanagedvalue.cpp @@ -774,9 +774,15 @@ bool QJSManagedValue::hasOwnProperty(const QString &name) const */ QJSValue QJSManagedValue::property(const QString &name) const { - if (!d || d->isNullOrUndefined()) + if (!d) return QJSValue(); + if (d->isNullOrUndefined()) { + QV4::ExecutionEngine *e = v4Engine(d); + e->throwTypeError(QStringLiteral("Cannot read property '%1' of null").arg(name)); + return QJSValue(); + } + if (QV4::String *string = d->as()) { if (name == QStringLiteral("length")) return QJSValue(string->d()->length()); @@ -801,6 +807,11 @@ void QJSManagedValue::setProperty(const QString &name, const QJSValue &value) if (!d) return; + if (d->isNullOrUndefined()) { + v4Engine(d)->throwTypeError( + QStringLiteral("Value is null and could not be converted to an object")); + } + if (QV4::Object *obj = d->as()) { QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value); if (Q_UNLIKELY(v4 && v4 != obj->engine())) { diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp index 75d3ccc12d..ce9164aa96 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp @@ -1624,13 +1624,12 @@ void tst_QJSManagedValue::engineDeleted() delete eng; + // You can still check the type, but anything involving the engine is obviously prohibited. QCOMPARE(v1.type(), QJSManagedValue::Undefined); QCOMPARE(v2.type(), QJSManagedValue::Undefined); QCOMPARE(v3.type(), QJSManagedValue::Undefined); QCOMPARE(v4.type(), QJSManagedValue::Undefined); QCOMPARE(v5.type(), QJSManagedValue::Undefined); - - QVERIFY(v3.property(QStringLiteral("foo")).isUndefined()); } void tst_QJSManagedValue::valueOfWithClosure() @@ -1822,4 +1821,36 @@ void tst_QJSManagedValue::jsMetaTypes() QVERIFY(halfPopulated.property("ccc").isUndefined()); } +void tst_QJSManagedValue::exceptionsOnNullAccess() +{ + QJSEngine engine; + QJSManagedValue null(QJSValue(QJSValue::NullValue), &engine); + QJSManagedValue undef(QJSValue(QJSValue::UndefinedValue), &engine); + + const QString nullReadError = engine.evaluate( + QStringLiteral("var n = null; n.prop")).toString(); + const QString nullWriteError = engine.evaluate( + QStringLiteral("var n = null; n.prop = 5")).toString(); + const QString undefReadError = engine.evaluate( + QStringLiteral("var n; n.prop")).toString(); + const QString undefWriteError = engine.evaluate( + QStringLiteral("var n; n.prop = 5")).toString(); + + QVERIFY(null.property(QStringLiteral("prop")).isUndefined()); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), nullReadError); + + null.setProperty(QStringLiteral("prop"), 5); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), nullWriteError); + + QVERIFY(undef.property(QStringLiteral("prop")).isUndefined()); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), undefReadError); + + undef.setProperty(QStringLiteral("prop"), 5); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), undefWriteError); +} + QTEST_MAIN(tst_QJSManagedValue) diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h index ea0d49589d..a7b757ac86 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h @@ -110,6 +110,8 @@ private Q_SLOTS: void stringByIndex(); void jsMetaTypes(); + void exceptionsOnNullAccess(); + private: void newEngine() { engine.reset(new QJSEngine()); } QScopedPointer engine;