aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Weickelt <richard@weickelt.de>2018-06-26 17:35:06 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-07-02 10:43:28 +0000
commit65b831c21afa8540bf5414191988f41bedfcb409 (patch)
treec0febd17f408c5b4bc3bc315869f7f3f438ca366
parente6d19ae5cf697087bbecce9d6f702afef7c29253 (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.cpp68
-rw-r--r--src/qml/jsapi/qjsengine.h2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp35
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"