diff options
author | Richard Weickelt <richard@weickelt.de> | 2018-06-26 17:35:06 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-07-02 10:43:28 +0000 |
commit | 65b831c21afa8540bf5414191988f41bedfcb409 (patch) | |
tree | c0febd17f408c5b4bc3bc315869f7f3f438ca366 | |
parent | e6d19ae5cf697087bbecce9d6f702afef7c29253 (diff) |
Add QJSEngine::throwError() method to report run-time errors
It is quite common in JavaScript to use exceptions for error handling, but
there was no way to generate an exception from C++ context, i.e. when
the JS run-time invoked a C++ method or a slot.
This patch adds an naive way to report run-time errors to QJSEngine from
CPP context. The user may set a custom error message, but the location
points always to the caller context in JavaScript.
[ChangeLog][QtQml][QJSEngine] Added API to throw run-time errors.
Task-number: QTBUG-39041
Change-Id: If59627b83d50351eb225adde63187fc251aa349e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 68 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 35 |
3 files changed, 105 insertions, 0 deletions
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index ebc35a8fd7..846ceeea9c 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -738,6 +738,74 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) \sa toScriptValue() */ +/*! + Throws a run-time error (exception) with the given \a message. + + This method is the C++ counterpart of a \c throw() expression in + JavaScript. It enables C++ code to report run-time errors to QJSEngine. + Therefore it should only be called from C++ code that was invoked by a + JavaScript function through QJSEngine. + + When returning from C++, the engine will interrupt the normal flow of + execution and call the the next pre-registered exception handler with + an error object that contains the given \a message. The error object + will point to the location of the top-most context on the JavaScript + caller stack; specifically, it will have properties \c lineNumber, + \c fileName and \c stack. These properties are described in + \l{Script Exceptions}. + + In the following example a C++ method in \e FileAccess.cpp throws an error + in \e qmlFile.qml at the position where \c readFileAsText() is called: + + \code + // qmlFile.qml + function someFunction() { + ... + var text = FileAccess.readFileAsText("/path/to/file.txt"); + } + \endcode + + \code + // FileAccess.cpp + // Assuming that FileAccess is a QObject-derived class that has been + // registered as a singleton type and provides an invokable method + // readFileAsText() + + QJSValue FileAccess::readFileAsText(const QString & filePath) { + QFile file(filePath); + + if (!file.open(QIODevice::ReadOnly)) { + jsEngine->throwError(file.errorString()); + return QString(); + } + + ... + return content; + } + \endcode + + It is also possible to catch the thrown error in JavaScript: + \code + // qmlFile.qml + function someFunction() { + ... + var text; + try { + text = FileAccess.readFileAsText("/path/to/file.txt"); + } catch (error) { + console.warn("In " + error.fileName + ":" + "error.lineNumber" + + ": " + error.message); + } + } + \endcode + + \since Qt 5.12 + \sa {Script Exceptions} +*/ +void QJSEngine::throwError(const QString &message) +{ + m_v4Engine->throwError(message); +} QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) { diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 3ba2b52e89..36a3e475f2 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -111,6 +111,8 @@ public: QV4::ExecutionEngine *handle() const { return m_v4Engine; } + void throwError(const QString &message); + private: QJSValue create(int type, const void *ptr); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 796d9e9c34..c20937f9a1 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -219,6 +219,11 @@ private slots: void protoChanges_QTBUG68369(); void multilineStrings(); + void throwError(); + +public: + Q_INVOKABLE QJSValue throwingCppMethod(); + signals: void testSignal(); }; @@ -4291,6 +4296,36 @@ void tst_QJSEngine::multilineStrings() } +void tst_QJSEngine::throwError() +{ + QJSEngine engine; + QJSValue wrappedThis = engine.newQObject(this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + engine.globalObject().setProperty("testCase", wrappedThis); + + QJSValue result = engine.evaluate( + "function test(){\n" + "try {\n" + " return testCase.throwingCppMethod();\n" + "} catch (error) {\n" + " return error;\n" + "}\n" + "return \"not reached!\";\n" + "}\n" + "test();" + ); + QVERIFY(result.isError()); + QCOMPARE(result.property("lineNumber").toString(), "3"); + QCOMPARE(result.property("message").toString(), "blub"); + QVERIFY(!result.property("stack").isUndefined()); +} + +QJSValue tst_QJSEngine::throwingCppMethod() +{ + qjsEngine(this)->throwError("blub"); + return QJSValue(47); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" |